# Centron Database Script Rules These conventions **must** be followed for all database scripts in the `Centron.BusinessLogic.Administration.Scripts.ScriptMethods.Scripts` namespace. Scripts are used to update the SQL Server database by creating/altering tables, creating indexes, functions, triggers, and updating data when necessary. --- ## 1. Script Organization & Naming 1. **Placement** - Place all scripts in the directory: `src/backend/Centron.BL/Administration/Scripts/ScriptMethods/Scripts/` 2. **Naming Convention** - Name scripts using the pattern: `ScriptMethod{NUMBER}.cs` - The script number is managed in an external Excel file accessible through Teams - When creating a new script, use a placeholder for the number and update it with the next available number from the Excel file 3. **Class Structure** - Each script class must inherit from `BaseScriptMethod` - The class name must match the filename ```csharp internal class ScriptMethod11699 : BaseScriptMethod ``` ## 2. Script Implementation 1. **Required Method** - Override the `GetSqlQueries()` method from the base class - Return type should be `IEnumerable` - Use `yield return` statements to return SQL statements ```csharp public override IEnumerable GetSqlQueries() { yield return ScriptHelpers.AddColumnIfNotExists("dbo", "TableName", "ColumnName", "datatype"); } ``` 2. **Using ScriptHelpers** - Always use the `ScriptHelpers` class methods to generate SQL statements - This ensures consistency and safety in database operations - Common helper methods: - `AddColumnIfNotExists` - `DropColumnIfExists` - `ChangeColumnTypeIfExists` - `AddTableIfNotExists` - `AddIndexIfNotExists` - `CreateSpecialObjectAlterIfExists` - And many more in `ScriptHelpers.cs` ### Script Helper Note When using `ScriptHelpers.AddTableIfNotExists()` method in database scripts, the primary key column `I3D` is automatically created and does not need to be specified in the column list. The method internally handles the creation of: - The `I3D` [int] IDENTITY(1,1) NOT NULL column - The primary key constraint with appropriate clustering ### Standard Audit Columns - Add `CreatedByI3D`, `CreatedDate`, `IsDeleted`, `DeletedByI3D`, and `DeletedDate` to new NHibernate-managed domain tables according to the standard entity conventions. - Add `ChangedByI3D` and `ChangedDate` only when rows are modified after creation. - Write-once/read-only history tables do not need `Changed*` columns. Examples: chat messages, tool call history, immutable communication logs. ## 3. Common Script Scenarios 1. **Adding or Changing Columns** ```csharp // Adding a column yield return ScriptHelpers.AddColumnIfNotExists("dbo", "TableName", "ColumnName", "datatype", nullable: true/false); // Changing a column type yield return ScriptHelpers.ChangeColumnTypeIfExists("dbo", "TableName", "ColumnName", "new_datatype"); ``` 2. **Creating Tables** ```csharp // Basic usage with dbo schema implied yield return ScriptHelpers.AddTableIfNotExists("TableName", ("Column1", "int", false, null), ("Column2", "nvarchar(100)", false, null), ("Column3", "datetime2(0)", true, null)); // Explicit schema usage yield return ScriptHelpers.AddTableIfNotExists( schema: "dbo", table: "TableName", ("Column1", "int", false, null), ("Column2", "nvarchar(100)", false, null), ("IsActive", "bit", false, null)); ``` **Parameter explanation:** - Column format: (name, datatype, nullable, defaultValue) - For new NHibernate-managed tables, use `null` or an empty value for `defaultValue` - NHibernate writes all mapped columns on `INSERT`, so SQL defaults on new tables do not apply in normal application writes and only create unused SQL objects - Only consider SQL defaults when extending existing tables with new `NOT NULL` columns, where existing rows or non-NHibernate writers need a valid value - If `defaultValue` is empty, no default constraint will be created - The primary key column `I3D` is automatically created - Examples of justified default values: - For integers: "0", "1", etc. - For strings: "''" (empty string with single quotes) - For dates: "GETUTCDATE()" - For bits (boolean): "1" (true) or "0" (false) 3. **Creating Indexes** ```csharp yield return ScriptHelpers.AddIndexIfNotExists( table: "TableName", indexName: "IX_TableName_Column1_Column2", columns: new List<(string column, OrderDirection? orderDirection)> { ("Column1", OrderDirection.ASC), ("Column2", OrderDirection.ASC), }); ``` 4. **Executing SQL Statements Directly** ```csharp yield return @" UPDATE TableName SET Column1 = 'value' WHERE Condition = 1;"; ``` 5. **Creating/Altering Views, Functions, Triggers** ```csharp yield return ScriptHelpers.CreateSpecialObjectAlterIfExists("ViewName", "VIEW", @" CREATE VIEW dbo.ViewName AS SELECT * FROM TableName WHERE Condition = 1;"); ``` ## 4. Best Practices 1. **Script Independence** - Each script should be independent and idempotent - Use conditional checks like `IF EXISTS` and `IF NOT EXISTS` 2. **Script Safety** - Always use `ScriptHelpers` methods when available - When direct SQL is needed, ensure proper schema references and SQL injection protection 3. **Performance Considerations** - For large data operations, consider transaction management and batching - When modifying indexed columns, use `AlterColumnTypeIndexSafe` to preserve indexes 4. **Documentation** - Add comments to clarify complex operations - For significant schema changes, document the purpose in a comment 5. **Testing** - Test scripts in a development environment before applying to production - Verify the script achieves the intended changes without side effects ## 5. Examples Refer to these existing scripts for common patterns: - `ScriptMethod11699.cs` - Adding columns and changing column types - `ScriptMethod11698.cs` - Executing direct SQL update statements - `ScriptMethod11696.cs` - Creating new tables - `ScriptMethod11677.cs` - Creating indexes - `ScriptMethod11670.cs` - Creating and altering views (same approach for triggers, functions)