Skip to content

Commit 4aa7811

Browse files
authored
Merge pull request rails#52773 from sobrinho/ar-drop-tables
Allow drop_table to accept an array of table names
2 parents 0c6e24f + 1666bad commit 4aa7811

File tree

9 files changed

+79
-20
lines changed

9 files changed

+79
-20
lines changed

activerecord/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
* Allow `drop_table` to accept an array of table names.
2+
3+
This will let you to drop multiple tables in a single call.
4+
5+
```ruby
6+
ActiveRecord::Base.connection.drop_table(:users, :posts)
7+
```
8+
9+
*Gabriel Sobrinho*
10+
111
* Add support for PostgreSQL `IF NOT EXISTS` via the `:if_not_exists` option
212
on the `add_enum_value` method.
313

activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ def rename_table(table_name, new_name, **)
525525
raise NotImplementedError, "rename_table is not implemented"
526526
end
527527

528-
# Drops a table from the database.
528+
# Drops a table or tables from the database.
529529
#
530530
# [<tt>:force</tt>]
531531
# Set to +:cascade+ to drop dependent objects as well.
@@ -536,10 +536,12 @@ def rename_table(table_name, new_name, **)
536536
#
537537
# Although this command ignores most +options+ and the block if one is given,
538538
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
539-
# In that case, +options+ and the block will be used by #create_table.
540-
def drop_table(table_name, **options)
541-
schema_cache.clear_data_source_cache!(table_name.to_s)
542-
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
539+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
540+
def drop_table(*table_names, **options)
541+
table_names.each do |table_name|
542+
schema_cache.clear_data_source_cache!(table_name.to_s)
543+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
544+
end
543545
end
544546

545547
# Add a new +type+ column named +column_name+ to +table_name+.

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def rename_table(table_name, new_name, **options)
332332
rename_table_indexes(table_name, new_name, **options)
333333
end
334334

335-
# Drops a table from the database.
335+
# Drops a table or tables from the database.
336336
#
337337
# [<tt>:force</tt>]
338338
# Set to +:cascade+ to drop dependent objects as well.
@@ -346,10 +346,10 @@ def rename_table(table_name, new_name, **options)
346346
#
347347
# Although this command ignores most +options+ and the block if one is given,
348348
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
349-
# In that case, +options+ and the block will be used by create_table.
350-
def drop_table(table_name, **options)
351-
schema_cache.clear_data_source_cache!(table_name.to_s)
352-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
349+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
350+
def drop_table(*table_names, **options)
351+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
352+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
353353
end
354354

355355
def rename_index(table_name, old_name, new_name)

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ def drop_database(name) # :nodoc:
5454
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
5555
end
5656

57-
def drop_table(table_name, **options) # :nodoc:
58-
schema_cache.clear_data_source_cache!(table_name.to_s)
59-
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
57+
def drop_table(*table_names, **options) # :nodoc:
58+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
59+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
6060
end
6161

6262
# Returns true if schema exists.

activerecord/lib/active_record/migration.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ def initialize
355355
#
356356
# === Deletion
357357
#
358-
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
358+
# * <tt>drop_table(*names)</tt>: Drops the given tables.
359359
# * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
360360
# specified by the given arguments.
361361
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
@@ -602,7 +602,7 @@ def create_join_table(table_1, table_2, **options)
602602
end
603603
end
604604

605-
def drop_table(table_name, **options)
605+
def drop_table(*table_names, **options)
606606
if block_given?
607607
super { |t| yield compatible_table_definition(t) }
608608
else

activerecord/lib/active_record/migration/command_recorder.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,18 @@ def invert_create_table(args, &block)
202202
end
203203

204204
def invert_drop_table(args, &block)
205-
if args.last.is_a?(Hash)
206-
args.last.delete(:if_exists)
205+
options = args.extract_options!
206+
options.delete(:if_exists)
207+
208+
if args.size > 1
209+
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given a single table name."
207210
end
208-
if args.size == 1 && block == nil
211+
212+
if args.size == 1 && options == {} && block == nil
209213
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
210214
end
211-
super
215+
216+
super(args.push(options), &block)
212217
end
213218

214219
def invert_rename_table(args)

activerecord/test/cases/adapters/abstract_mysql_adapter/active_schema_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ def test_drop_table
118118
assert_equal "DROP TABLE `people`", drop_table(:people)
119119
end
120120

121+
def test_drop_tables
122+
assert_equal "DROP TABLE `people`, `sobrinho`", drop_table(:people, :sobrinho)
123+
end
124+
121125
def test_create_mysql_database_with_encoding
122126
if ActiveRecord::Base.lease_connection.send(:row_format_dynamic_by_default?)
123127
assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8mb4`", create_database(:matt)
@@ -147,6 +151,10 @@ def test_drop_table_with_specific_database
147151
assert_equal "DROP TABLE `otherdb`.`people`", drop_table("otherdb.people")
148152
end
149153

154+
def test_drop_tables_with_specific_database
155+
assert_equal "DROP TABLE `otherdb`.`people`, `otherdb`.`sobrinho`", drop_table("otherdb.people", "otherdb.sobrinho")
156+
end
157+
150158
def test_add_timestamps
151159
with_real_execute do
152160
ActiveRecord::Base.lease_connection.create_table :delete_me

activerecord/test/cases/migration/change_schema_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,24 @@ def test_drop_table_if_exists
467467
assert_not connection.table_exists?(:testings)
468468
end
469469

470+
def test_drop_tables_if_exists
471+
connection.create_table(:testings)
472+
connection.create_table(:sobrinho)
473+
assert connection.table_exists?(:testings)
474+
assert connection.table_exists?(:sobrinho)
475+
connection.drop_table(:testings, :sobrinho, if_exists: true)
476+
assert_not connection.table_exists?(:testings)
477+
assert_not connection.table_exists?(:sobrinho)
478+
end
479+
470480
def test_drop_table_if_exists_nothing_raised
471481
assert_nothing_raised { connection.drop_table(:nonexistent, if_exists: true) }
472482
end
473483

484+
def test_drop_tables_if_exists_nothing_raised
485+
assert_nothing_raised { connection.drop_table(:nonexistent, :nonexistent_sobrinho, if_exists: true) }
486+
end
487+
474488
private
475489
def testing_table_with_only_foo_attribute
476490
connection.create_table :testings, id: false do |t|

activerecord/test/cases/migration/command_recorder_test.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,31 @@ def test_invert_drop_table_with_if_exists
172172
end
173173

174174
def test_invert_drop_table_without_a_block_nor_option
175-
assert_raises(ActiveRecord::IrreversibleMigration) do
175+
assert_raises(ActiveRecord::IrreversibleMigration, match: "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty).") do
176176
@recorder.inverse_of :drop_table, [:people_reminders]
177177
end
178178
end
179179

180+
def test_invert_drop_table_with_multiple_tables
181+
assert_raises(ActiveRecord::IrreversibleMigration, match: "To avoid mistakes, drop_table is only reversible if given a single table name.") do
182+
@recorder.inverse_of :drop_table, [:musics, :artists]
183+
end
184+
end
185+
186+
def test_invert_drop_table_with_multiple_tables_and_options
187+
assert_raises(ActiveRecord::IrreversibleMigration, match: "To avoid mistakes, drop_table is only reversible if given a single table name.") do
188+
@recorder.inverse_of :drop_table, [:musics, :artists, id: false]
189+
end
190+
end
191+
192+
def test_invert_drop_table_with_multiple_tables_and_block
193+
block = Proc.new { }
194+
195+
assert_raises(ActiveRecord::IrreversibleMigration, match: "To avoid mistakes, drop_table is only reversible if given a single table name.") do
196+
@recorder.inverse_of :drop_table, [:musics, :artists], &block
197+
end
198+
end
199+
180200
def test_invert_create_join_table
181201
drop_join_table = @recorder.inverse_of :create_join_table, [:musics, :artists]
182202
assert_equal [:drop_join_table, [:musics, :artists], nil], drop_join_table

0 commit comments

Comments
 (0)