diff --git a/spec/avram/migrator/alter_table_statement_spec.cr b/spec/avram/migrator/alter_table_statement_spec.cr index 89efd9551..eba721b79 100644 --- a/spec/avram/migrator/alter_table_statement_spec.cr +++ b/spec/avram/migrator/alter_table_statement_spec.cr @@ -186,4 +186,24 @@ describe Avram::Migrator::AlterTableStatement do end end end + + context "IF EXISTS" do + it "adds the option to the table" do + built = Avram::Migrator::AlterTableStatement.new(:users, if_exists: true).build do + add name : String? + rename :old_name, :new_name + rename_belongs_to :owner, :boss + end + + built.statements.size.should eq 3 + + built.statements[0].should eq "ALTER TABLE IF EXISTS users RENAME COLUMN old_name TO new_name;" + built.statements[1].should eq "ALTER TABLE IF EXISTS users RENAME COLUMN owner_id TO boss_id;" + + built.statements[2].should eq <<-SQL + ALTER TABLE IF EXISTS users + ADD name text; + SQL + end + end end diff --git a/spec/avram/migrator/create_table_statement_spec.cr b/spec/avram/migrator/create_table_statement_spec.cr index 824a5047e..93219355b 100644 --- a/spec/avram/migrator/create_table_statement_spec.cr +++ b/spec/avram/migrator/create_table_statement_spec.cr @@ -236,4 +236,16 @@ describe Avram::Migrator::CreateTableStatement do end end end + + context "IF NOT EXISTS" do + it "adds the option to the table" do + built = Avram::Migrator::CreateTableStatement.new(:users, if_not_exists: true).build do + end + + built.statements.size.should eq 1 + built.statements.first.should eq <<-SQL + CREATE TABLE IF NOT EXISTS users (\n); + SQL + end + end end diff --git a/spec/avram/migrator/migration_spec.cr b/spec/avram/migrator/migration_spec.cr index fecc26a1d..86f99f7ab 100644 --- a/spec/avram/migrator/migration_spec.cr +++ b/spec/avram/migrator/migration_spec.cr @@ -64,6 +64,20 @@ class MigrationWithFunctionAndTrigger::V996 < Avram::Migrator::Migration::V1 end end +class MigrationCreateTableIfNotExists::V995 < Avram::Migrator::Migration::V1 + def migrate + create :fake_things, if_not_exists: true do + add foo : String + end + end + + def rollback + alter :fake_things, if_exists: true do + add foo : String? + end + end +end + describe Avram::Migrator::Migration::V1 do it "executes statements in a transaction" do expect_raises Avram::FailedMigration do @@ -113,6 +127,19 @@ describe Avram::Migrator::Migration::V1 do sql.should contain "CREATE TRIGGER trigger_touch_updated_at" end end + + describe "if _not_ exists" do + it "passes the option through the macro" do + migration = MigrationCreateTableIfNotExists::V995.new + migration.migrate + sql = migration.prepared_statements.join("\n") + sql.should contain "CREATE TABLE IF NOT EXISTS fake_things" + + migration.rollback + sql = migration.prepared_statements.join("\n") + sql.should contain "ALTER TABLE IF EXISTS fake_things" + end + end end private def get_column_names(table_name) diff --git a/src/avram/migrator/alter_table_statement.cr b/src/avram/migrator/alter_table_statement.cr index 575709401..0c04cae50 100644 --- a/src/avram/migrator/alter_table_statement.cr +++ b/src/avram/migrator/alter_table_statement.cr @@ -11,8 +11,9 @@ class Avram::Migrator::AlterTableStatement getter fill_existing_with_statements = [] of String getter change_type_statements = [] of String getter change_default_statements = [] of String + private getter? if_exists : Bool = false - def initialize(@table_name : TableName) + def initialize(@table_name : TableName, *, @if_exists : Bool = false) end # Change the column's type from whatever it is currently to @@ -116,13 +117,19 @@ class Avram::Migrator::AlterTableStatement alter_statements + change_default_statements + change_type_statements + index_statements + fill_existing_with_statements end + def if_exists_statement + if if_exists? + "IF EXISTS " + end + end + def alter_statements : Array(String) alterations = renamed_rows.map do |statement| - "ALTER TABLE #{@table_name} #{statement};" + "ALTER TABLE #{if_exists_statement}#{@table_name} #{statement};" end unless (rows + dropped_rows).empty? alterations << String.build do |statement| - statement << "ALTER TABLE #{@table_name}" + statement << "ALTER TABLE #{if_exists_statement}#{@table_name}" statement << "\n" statement << (rows + dropped_rows).join(",\n") statement << ';' @@ -249,7 +256,7 @@ class Avram::Migrator::AlterTableStatement def add_fill_existing_with_statements(column : Symbol | String, type, value, nilable) @fill_existing_with_statements << "UPDATE #{@table_name} SET #{column} = #{value};" - @fill_existing_with_statements << "ALTER TABLE #{@table_name} ALTER COLUMN #{column} SET NOT NULL;" unless nilable + @fill_existing_with_statements << "ALTER TABLE #{if_exists_statement}#{@table_name} ALTER COLUMN #{column} SET NOT NULL;" unless nilable end macro symbol_expected_error(action, name) diff --git a/src/avram/migrator/create_table_statement.cr b/src/avram/migrator/create_table_statement.cr index 92a9b55d0..735004920 100644 --- a/src/avram/migrator/create_table_statement.cr +++ b/src/avram/migrator/create_table_statement.cr @@ -7,8 +7,9 @@ class Avram::Migrator::CreateTableStatement private getter rows = [] of String private getter constraints = [] of String + private getter? if_not_exists : Bool = false - def initialize(@table_name : TableName) + def initialize(@table_name : TableName, *, @if_not_exists : Bool = false) end # Accepts a block to build a table and indices using `add` and `add_index` methods. @@ -54,7 +55,7 @@ class Avram::Migrator::CreateTableStatement private def table_statement String.build do |statement| - statement << initial_table_statement + initial_table_statement(statement) statement << rows.join(",\n") statement << ",\n" if !constraints.empty? statement << constraints.join(", \n") @@ -62,10 +63,11 @@ class Avram::Migrator::CreateTableStatement end end - private def initial_table_statement - <<-SQL - CREATE TABLE #{@table_name} (\n - SQL + private def initial_table_statement(io) + io << "CREATE TABLE " + io << "IF NOT EXISTS " if if_not_exists? + io << @table_name + io << " (\n" end macro primary_key(type_declaration) diff --git a/src/avram/migrator/statement_helpers.cr b/src/avram/migrator/statement_helpers.cr index 5571c3b10..5342169c6 100644 --- a/src/avram/migrator/statement_helpers.cr +++ b/src/avram/migrator/statement_helpers.cr @@ -4,8 +4,8 @@ module Avram::Migrator::StatementHelpers include Avram::Migrator::IndexStatementHelpers include Avram::TableFor - macro create(table_name) - statements = Avram::Migrator::CreateTableStatement.new({{ table_name }}).build do + macro create(table_name, *, if_not_exists = false) + statements = Avram::Migrator::CreateTableStatement.new({{ table_name }}, if_not_exists: {{ if_not_exists }}).build do {{ yield }} end.statements @@ -18,8 +18,8 @@ module Avram::Migrator::StatementHelpers prepared_statements << Avram::Migrator::DropTableStatement.new(table_name).build end - macro alter(table_name) - statements = Avram::Migrator::AlterTableStatement.new({{ table_name }}).build do + macro alter(table_name, *, if_exists = false) + statements = Avram::Migrator::AlterTableStatement.new({{ table_name }}, if_exists: {{ if_exists }}).build do {{ yield }} end.statements