using Invercargill.DataStructures; using InvercargillSql.Orm; namespace InvercargillSql.Migrations { /** * Builder for CREATE TABLE operations that provides a fluent API for defining table structure. * * This builder is used within MigrationBuilder.create_table() to define columns, constraints, * and indexes. It follows the builder pattern, returning itself for chaining table-level operations. * * Example usage within a migration: * {{{ * b.create_table("users", t => { * t.column("username", c => c.type_text().not_null()) * t.column("id", c => c.type_int().primary_key().auto_increment()) * t.index("idx_email").on_column("email") * t.unique("uq_users_username", {"username"}) * }); * }}} */ public class TableBuilder : Object { private MigrationBuilder _parent; private string _table_name; private Vector _columns; private Vector _constraints; private Vector _indexes; private Vector _column_builders; /** * Creates a new TableBuilder for the specified table. * * @param parent the parent MigrationBuilder to return to after table definition * @param table_name the name of the table to create */ internal TableBuilder(MigrationBuilder parent, string table_name) { _parent = parent; _table_name = table_name; _columns = new Vector(); _constraints = new Vector(); _indexes = new Vector(); _column_builders = new Vector(); } /** * Adds a new column to the table definition. * * The generic type T is used to infer the column type. The returned * MigrationColumnBuilder can be used to configure additional column properties. * * Note: The builder is tracked internally to collect FK constraints and index * operations when build_operation() is called. * * @param name the name of the column * @return a MigrationColumnBuilder for configuring the column */ public MigrationColumnBuilder column(string name) { var col = new ColumnDefinition() { name = name, column_type = ColumnType.from_gtype(typeof(T)) ?? ColumnType.TEXT }; _columns.add(col); var builder = new MigrationColumnBuilder.for_table(this, col); _column_builders.add(builder); return builder; } /** * Creates an index on this table. * * Use the returned IndexBuilder to specify columns and uniqueness. * * @param name the name of the index * @return an IndexBuilder for configuring the index * * Example: * {{{ * t.index("idx_email").on_column("email") * t.index("idx_composite").on_columns("email", "name").unique() * }}} */ public IndexBuilder index(string name) { return new IndexBuilder.for_table(this, _table_name, name); } /** * Adds a PRIMARY KEY constraint to the table. * * For single-column primary keys, you can also use MigrationColumnBuilder.primary_key(). * * @param columns the column names that form the primary key * @return this builder for method chaining */ public TableBuilder primary_key(string[] columns) { var constraint = new TableConstraint() { constraint_type = "PRIMARY KEY" }; foreach (var col in columns) { constraint.columns.add(col); } _constraints.add(constraint); return this; } /** * Adds a UNIQUE constraint to the table. * * @param name the name of the constraint * @param columns the column names that must be unique together * @return this builder for method chaining */ public TableBuilder unique(string name, string[] columns) { var constraint = new TableConstraint() { name = name, constraint_type = "UNIQUE" }; foreach (var col in columns) { constraint.columns.add(col); } _constraints.add(constraint); return this; } /** * Adds a FOREIGN KEY constraint to the table. * * @param name the name of the constraint * @param columns the column names in this table * @param ref_table the name of the referenced table * @param ref_columns the column names in the referenced table * @return this builder for method chaining */ public TableBuilder foreign_key(string name, string[] columns, string ref_table, string[] ref_columns) { var constraint = new TableConstraint() { name = name, constraint_type = "FOREIGN KEY", reference_table = ref_table }; foreach (var col in columns) { constraint.columns.add(col); } foreach (var col in ref_columns) { constraint.reference_columns.add(col); } _constraints.add(constraint); return this; } /** * Adds a column definition directly to the table. * * This is used internally by MigrationColumnBuilder. * * @param col the column definition to add */ internal void add_column(ColumnDefinition col) { _columns.add(col); } /** * Adds an index operation to the table. * * This is used internally by IndexBuilder. * * @param op the index operation to add */ internal void add_index_operation(CreateIndexOperation op) { _indexes.add(op); } /** * Returns to the parent MigrationBuilder. * * This method is used by MigrationColumnBuilder to navigate back to the * MigrationBuilder after configuring a column. * * @return the parent MigrationBuilder */ internal MigrationBuilder return_to_migration() { return _parent; } /** * Builds the CreateTableOperation from this builder's configuration. * * This method also collects FK constraints and index operations from column builders. * * @return a new CreateTableOperation containing all columns and constraints */ internal CreateTableOperation build_operation() { var op = new CreateTableOperation() { table_name = _table_name }; foreach (var col in _columns) { op.columns.add(col); } foreach (var constraint in _constraints) { op.constraints.add(constraint); } // Collect FK constraints and indexes from column builders for (int i = 0; i < (int) _column_builders.length; i++) { var builder = _column_builders.get(i); var col = _columns.get(i); // Collect FK constraint if configured var fk_builder = builder.fk_builder; if (fk_builder != null) { var constraint = fk_builder.build_constraint(col.name, _table_name); op.constraints.add(constraint); } // Collect index operation if configured if (builder.should_create_index) { var index_op = new CreateIndexOperation() { table_name = _table_name, is_unique = col.is_unique }; index_op.columns.add(col.name); // Use custom index name or auto-generate index_op.index_name = builder.index_name ?? @"idx_$(_table_name)_$(col.name)"; _indexes.add(index_op); } } return op; } /** * Gets all index operations collected by this builder. * * This includes indexes created via index() and indexes from column definitions. * * @return a Vector of CreateIndexOperation objects */ internal Vector get_index_operations() { return _indexes; } } }