migration-builder.vala 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. using Invercargill.DataStructures;
  2. using InvercargillSql.Dialects;
  3. using InvercargillSql.Orm;
  4. namespace InvercargillSql.Migrations {
  5. /**
  6. * Delegate for configuring table creation within a migration.
  7. *
  8. * @param t the TableBuilder to configure
  9. */
  10. public delegate void TableConfig(TableBuilder t);
  11. /**
  12. * Delegate for configuring table alterations within a migration.
  13. *
  14. * @param t the AlterTableBuilder to configure
  15. */
  16. public delegate void AlterConfig(AlterTableBuilder t);
  17. /**
  18. * Main builder class providing the fluent API for defining migration operations.
  19. *
  20. * MigrationBuilder is the central class used in Migration.up() and Migration.down()
  21. * methods to define schema changes. It supports creating/dropping tables, altering
  22. * table structure, and executing raw SQL.
  23. *
  24. * Index creation is now handled within TableBuilder and AlterTableBuilder using
  25. * the fluent index() method.
  26. *
  27. * All methods return the builder instance for method chaining.
  28. *
  29. * Example usage:
  30. * {{{
  31. * public override void up(MigrationBuilder b) {
  32. * b.create_table("users", t => {
  33. * t.column<int>("id", c => c.type_int().primary_key().auto_increment());
  34. * t.column<string>("email", c => c.type_text().not_null().unique());
  35. * t.column<string>("name", c => c.type_text().not_null());
  36. * t.index("idx_users_email").on_column("email");
  37. * });
  38. * }
  39. *
  40. * public override void down(MigrationBuilder b) {
  41. * b.drop_table("users"); // Indexes are dropped automatically with the table
  42. * }
  43. * }}}
  44. */
  45. public class MigrationBuilder : Object {
  46. private SqlDialect _dialect;
  47. private Vector<SchemaOperation> _operations;
  48. /**
  49. * Creates a new MigrationBuilder for the specified SQL dialect.
  50. *
  51. * @param dialect the SQL dialect to use for generating SQL statements
  52. */
  53. public MigrationBuilder(SqlDialect dialect) {
  54. _dialect = dialect;
  55. _operations = new Vector<SchemaOperation>();
  56. }
  57. /**
  58. * Creates a new table with the specified name and configuration.
  59. *
  60. * The configuration delegate receives a TableBuilder that can be used
  61. * to define columns, constraints, and indexes.
  62. *
  63. * Indexes defined within the TableBuilder are emitted after the CREATE TABLE
  64. * statement in the generated SQL.
  65. *
  66. * @param name the name of the table to create
  67. * @param config a delegate that configures the table structure
  68. * @return this builder for method chaining
  69. */
  70. public MigrationBuilder create_table(string name, owned TableConfig config) {
  71. var builder = new TableBuilder(this, name);
  72. config(builder);
  73. // Add the CREATE TABLE operation
  74. _operations.add(builder.build_operation());
  75. // Add any index operations defined within the table builder
  76. foreach (var index_op in builder.get_index_operations()) {
  77. _operations.add(index_op);
  78. }
  79. return this;
  80. }
  81. /**
  82. * Drops the specified table.
  83. *
  84. * Note: In most databases, dropping a table automatically drops all
  85. * associated indexes.
  86. *
  87. * @param name the name of the table to drop
  88. * @return this builder for method chaining
  89. */
  90. public MigrationBuilder drop_table(string name) {
  91. _operations.add(new DropTableOperation() { table_name = name });
  92. return this;
  93. }
  94. /**
  95. * Alters an existing table with the specified configuration.
  96. *
  97. * The configuration delegate receives an AlterTableBuilder that can be used
  98. * to add, drop, or rename columns, and to create or drop indexes.
  99. *
  100. * @param name the name of the table to alter
  101. * @param config a delegate that configures the alterations
  102. * @return this builder for method chaining
  103. */
  104. public MigrationBuilder alter_table(string name, owned AlterConfig config) {
  105. var builder = new AlterTableBuilder(this, name);
  106. config(builder);
  107. foreach (var op in builder.get_operations()) {
  108. _operations.add(op);
  109. }
  110. return this;
  111. }
  112. /**
  113. * Executes raw SQL directly.
  114. *
  115. * Use with caution as this bypasses the dialect abstraction.
  116. *
  117. * @param sql the raw SQL to execute
  118. * @return this builder for method chaining
  119. */
  120. public MigrationBuilder execute_sql(string sql) {
  121. _operations.add(new RawSqlOperation() { sql = sql });
  122. return this;
  123. }
  124. /**
  125. * Adds a column operation from a column builder.
  126. *
  127. * This is used internally by MigrationColumnBuilder to add configured
  128. * columns directly to the operations list.
  129. *
  130. * @param table_name the name of the table
  131. * @param col the column definition to add
  132. */
  133. internal void add_column_from_builder(string table_name, ColumnDefinition col) {
  134. _operations.add(new AddColumnOperation() {
  135. table_name = table_name,
  136. column = col
  137. });
  138. }
  139. /**
  140. * Gets all schema operations collected by this builder.
  141. *
  142. * @return a Vector of SchemaOperation objects
  143. */
  144. public Vector<SchemaOperation> get_operations() {
  145. return _operations;
  146. }
  147. /**
  148. * Executes all collected operations against the specified connection.
  149. *
  150. * Each operation is converted to SQL using the dialect and executed
  151. * in the order they were added.
  152. *
  153. * @param conn the database connection to execute against
  154. * @throws SqlError if any operation fails
  155. */
  156. public void execute(Connection conn) throws SqlError {
  157. foreach (var op in _operations) {
  158. var sql = op.to_sql(_dialect);
  159. conn.execute(sql);
  160. }
  161. }
  162. }
  163. }