aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew White <pixeltrix@users.noreply.github.com>2018-11-08 14:44:16 +0000
committerGitHub <noreply@github.com>2018-11-08 14:44:16 +0000
commit4fe27cdc15b88fed5ae725a886b7e96202f649a8 (patch)
tree29a476831be0fe4574a41b646d903540d8495fde
parentedea469d2876e73e336411c695f64b7286c58167 (diff)
parent6e0ff0053702cd1dc0f149f4ca59f968a2560d72 (diff)
downloadrails-4fe27cdc15b88fed5ae725a886b7e96202f649a8.tar.gz
rails-4fe27cdc15b88fed5ae725a886b7e96202f649a8.tar.bz2
rails-4fe27cdc15b88fed5ae725a886b7e96202f649a8.zip
Merge pull request #31382 from fatkodima/create_table-if_not_exists
Add an :if_not_exists option to create_table
-rw-r--r--activerecord/CHANGELOG.md19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb5
-rw-r--r--activerecord/test/cases/migration_test.rb30
5 files changed, 59 insertions, 4 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f57ca1854f..2a237f86cf 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,22 @@
+* Add an `:if_not_exists` option to `create_table`.
+
+ Example:
+
+ create_table :posts, if_not_exists: true do |t|
+ t.string :title
+ end
+
+ That would execute:
+
+ CREATE TABLE IF NOT EXISTS posts (
+ ...
+ )
+
+ If the table already exists, `if_not_exists: false` (the default) raises an
+ exception whereas `if_not_exists: true` does nothing.
+
+ *fatkodima*, *Stefan Kanev*
+
* Defining an Enum as a Hash with blank key, or as an Array with a blank value, now raises an `ArgumentError`.
*Christophe Maximin*
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 9d9e8a4110..19ff780192 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -39,7 +39,9 @@ module ActiveRecord
end
def visit_TableDefinition(o)
- create_sql = +"CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
+ create_sql = +"CREATE#{' TEMPORARY' if o.temporary} TABLE "
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
+ create_sql << "#{quote_table_name(o.name)} "
statements = o.columns.map { |c| accept c }
statements << accept(o.primary_keys) if o.primary_keys
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 70607fda5a..ca99a2d983 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -257,14 +257,15 @@ module ActiveRecord
include ColumnMethods
attr_accessor :indexes
- attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :foreign_keys, :comment
- def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
+ def initialize(name, temporary = false, if_not_exists = false, options = nil, as = nil, comment: nil)
@columns_hash = {}
@indexes = []
@foreign_keys = []
@primary_keys = nil
@temporary = temporary
+ @if_not_exists = if_not_exists
@options = options
@as = as
@name = name
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 8a7020a799..295ad89f7c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -205,6 +205,9 @@ module ActiveRecord
# Set to true to drop the table before creating it.
# Set to +:cascade+ to drop dependent objects as well.
# Defaults to false.
+ # [<tt>:if_not_exists</tt>]
+ # Set to true to avoid raising an error when the table already exists.
+ # Defaults to false.
# [<tt>:as</tt>]
# SQL to use to generate the table. When this option is used, the block is
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
@@ -288,7 +291,7 @@ module ActiveRecord
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, comment: nil, **options)
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
+ td = create_table_definition table_name, options[:temporary], options[:if_not_exists], options[:options], options[:as], comment: comment
if options[:id] != false && !options[:as]
pk = options.fetch(:primary_key) do
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 5d060c8899..661163b4a1 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -127,6 +127,36 @@ class MigrationTest < ActiveRecord::TestCase
assert_equal 20131219224947, migrator.current_version
end
+ def test_create_table_raises_if_already_exists
+ connection = Person.connection
+ connection.create_table :testings, force: true do |t|
+ t.string :foo
+ end
+
+ assert_raise(ActiveRecord::StatementInvalid) do
+ connection.create_table :testings do |t|
+ t.string :foo
+ end
+ end
+ ensure
+ connection.drop_table :testings, if_exists: true
+ end
+
+ def test_create_table_with_if_not_exists_true
+ connection = Person.connection
+ connection.create_table :testings, force: true do |t|
+ t.string :foo
+ end
+
+ assert_nothing_raised do
+ connection.create_table :testings, if_not_exists: true do |t|
+ t.string :foo
+ end
+ end
+ ensure
+ connection.drop_table :testings, if_exists: true
+ end
+
def test_create_table_with_force_true_does_not_drop_nonexisting_table
# using a copy as we need the drop_table method to
# continue to work for the ensure block of the test