using Invercargill.DataStructures; using InvercargillSql.Dialects; using InvercargillSql.Orm; namespace InvercargillSql.Migrations { /** * Delegate for configuring table creation within a migration. * * @param t the TableBuilder to configure */ public delegate void TableConfig(TableBuilder t); /** * Delegate for configuring table alterations within a migration. * * @param t the AlterTableBuilder to configure */ public delegate void AlterConfig(AlterTableBuilder t); /** * Main builder class providing the fluent API for defining migration operations. * * MigrationBuilder is the central class used in Migration.up() and Migration.down() * methods to define schema changes. It supports creating/dropping tables, altering * table structure, and executing raw SQL. * * Index creation is now handled within TableBuilder and AlterTableBuilder using * the fluent index() method. * * All methods return the builder instance for method chaining. * * Example usage: * {{{ * public override void up(MigrationBuilder b) { * b.create_table("users", t => { * t.column("id", c => c.type_int().primary_key().auto_increment()); * t.column("email", c => c.type_text().not_null().unique()); * t.column("name", c => c.type_text().not_null()); * t.index("idx_users_email").on_column("email"); * }); * } * * public override void down(MigrationBuilder b) { * b.drop_table("users"); // Indexes are dropped automatically with the table * } * }}} */ public class MigrationBuilder : Object { private SqlDialect _dialect; private Vector _operations; /** * Creates a new MigrationBuilder for the specified SQL dialect. * * @param dialect the SQL dialect to use for generating SQL statements */ public MigrationBuilder(SqlDialect dialect) { _dialect = dialect; _operations = new Vector(); } /** * Creates a new table with the specified name and configuration. * * The configuration delegate receives a TableBuilder that can be used * to define columns, constraints, and indexes. * * Indexes defined within the TableBuilder are emitted after the CREATE TABLE * statement in the generated SQL. * * @param name the name of the table to create * @param config a delegate that configures the table structure * @return this builder for method chaining */ public MigrationBuilder create_table(string name, owned TableConfig config) { var builder = new TableBuilder(this, name); config(builder); // Add the CREATE TABLE operation _operations.add(builder.build_operation()); // Add any index operations defined within the table builder foreach (var index_op in builder.get_index_operations()) { _operations.add(index_op); } return this; } /** * Drops the specified table. * * Note: In most databases, dropping a table automatically drops all * associated indexes. * * @param name the name of the table to drop * @return this builder for method chaining */ public MigrationBuilder drop_table(string name) { _operations.add(new DropTableOperation() { table_name = name }); return this; } /** * Alters an existing table with the specified configuration. * * The configuration delegate receives an AlterTableBuilder that can be used * to add, drop, or rename columns, and to create or drop indexes. * * @param name the name of the table to alter * @param config a delegate that configures the alterations * @return this builder for method chaining */ public MigrationBuilder alter_table(string name, owned AlterConfig config) { var builder = new AlterTableBuilder(this, name); config(builder); foreach (var op in builder.get_operations()) { _operations.add(op); } return this; } /** * Executes raw SQL directly. * * Use with caution as this bypasses the dialect abstraction. * * @param sql the raw SQL to execute * @return this builder for method chaining */ public MigrationBuilder execute_sql(string sql) { _operations.add(new RawSqlOperation() { sql = sql }); return this; } /** * Adds a column operation from a column builder. * * This is used internally by MigrationColumnBuilder to add configured * columns directly to the operations list. * * @param table_name the name of the table * @param col the column definition to add */ internal void add_column_from_builder(string table_name, ColumnDefinition col) { _operations.add(new AddColumnOperation() { table_name = table_name, column = col }); } /** * Gets all schema operations collected by this builder. * * @return a Vector of SchemaOperation objects */ public Vector get_operations() { return _operations; } /** * Executes all collected operations against the specified connection. * * Each operation is converted to SQL using the dialect and executed * in the order they were added. * * @param conn the database connection to execute against * @throws SqlError if any operation fails */ public void execute(Connection conn) throws SqlError { foreach (var op in _operations) { var sql = op.to_sql(_dialect); conn.execute(sql); } } } }