aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorBogdan <bogdanvlviv@gmail.com>2019-03-05 00:57:38 +0200
committerDavid Heinemeier Hansson <david@loudthinking.com>2019-03-04 14:57:38 -0800
commita8c0ebccbdbf4e2ccbacbf94cba6cf24699af190 (patch)
tree0767c1a1138c4a670384fc8bc6a1e3ac7b23e879 /activerecord
parent076e8eddf4766a27a62fd0ae087c6c08501ddc42 (diff)
downloadrails-a8c0ebccbdbf4e2ccbacbf94cba6cf24699af190.tar.gz
rails-a8c0ebccbdbf4e2ccbacbf94cba6cf24699af190.tar.bz2
rails-a8c0ebccbdbf4e2ccbacbf94cba6cf24699af190.zip
Allow `truncate` for SQLite3 adapter and add `rails db:seed:replant` (#34779)
* Add `ActiveRecord::Base.connection.truncate` for SQLite3 adapter. SQLite doesn't support `TRUNCATE TABLE`, but SQLite3 adapter can support `ActiveRecord::Base.connection.truncate` by using `DELETE FROM`. `DELETE` without `WHERE` uses "The Truncate Optimization", see https://www.sqlite.org/lang_delete.html. * Add `rails db:seed:replant` that truncates database tables and loads the seeds Closes #34765
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md9
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb18
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb10
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb12
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb10
-rw-r--r--activerecord/test/cases/adapters/sqlite3/connection_test.rb18
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb122
9 files changed, 213 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 979b48c654..04449a3ebc 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,12 @@
+* Add `rails db:seed:replant` that truncates tables of each database
+ for current environment and loads the seeds.
+
+ *bogdanvlviv*, *DHH*
+
+* Add `ActiveRecord::Base.connection.truncate` for SQLite3 adapter.
+
+ *bogdanvlviv*
+
* Deprecate mismatched collation comparison for uniqueness validator.
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 9f28fdf749..63e07932d9 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -154,6 +154,10 @@ module ActiveRecord
@statements.clear
end
+ def truncate(table_name, name = nil)
+ execute "DELETE FROM #{quote_table_name(table_name)}", name
+ end
+
def supports_index_sort_order?
true
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 8de06e8466..f021a8f6c4 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -66,6 +66,11 @@ db_namespace = namespace :db do
end
end
+ # desc "Truncates tables of each database for current environment"
+ task truncate_all: [:load_config, :check_protected_environments] do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all
+ end
+
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
task purge: [:load_config, :check_protected_environments] do
ActiveRecord::Tasks::DatabaseTasks.purge_current
@@ -223,6 +228,11 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.load_seed
end
+ namespace :seed do
+ desc "Truncates tables of each database for current environment and loads the seeds"
+ task replant: [:load_config, :truncate_all, :seed]
+ end
+
namespace :fixtures do
desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task load: :load_config do
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 27e401a756..ef02ca8190 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -182,6 +182,24 @@ module ActiveRecord
}
end
+ def truncate_tables(configuration)
+ ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
+ table_names = ActiveRecord::Base.connection.tables
+ internal_table_names = [
+ ActiveRecord::Base.schema_migrations_table_name,
+ ActiveRecord::Base.internal_metadata_table_name
+ ]
+
+ class_for_adapter(configuration["adapter"]).new(configuration).truncate_tables(*table_names.without(*internal_table_names))
+ end
+ end
+
+ def truncate_all(environment = env)
+ ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
+ truncate_tables db_config.config
+ end
+ end
+
def migrate
check_target_version
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 1c1b29b5e1..f2b4ead98d 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -31,6 +31,16 @@ module ActiveRecord
connection.recreate_database configuration["database"], creation_options
end
+ def truncate_tables(*table_names)
+ return if table_names.empty?
+
+ ActiveRecord::Base.connection.disable_referential_integrity do
+ table_names.each do |table_name|
+ ActiveRecord::Base.connection.truncate(table_name)
+ end
+ end
+ end
+
def charset
connection.charset
end
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index 8acb11f75f..dc368eb97d 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -48,6 +48,18 @@ module ActiveRecord
create true
end
+ def truncate_tables(*table_names)
+ return if table_names.empty?
+
+ ActiveRecord::Base.connection.disable_referential_integrity do
+ quoted_table_names = table_names.map do |table_name|
+ ActiveRecord::Base.connection.quote_table_name(table_name)
+ end
+
+ ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{quoted_table_names.join(", ")}"
+ end
+ end
+
def structure_dump(filename, extra_flags)
set_psql_env
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index a82cea80ca..cedbae6b7f 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -33,6 +33,16 @@ module ActiveRecord
create
end
+ def truncate_tables(*table_names)
+ return if table_names.empty?
+
+ ActiveRecord::Base.connection.disable_referential_integrity do
+ table_names.each do |table_name|
+ ActiveRecord::Base.connection.truncate(table_name)
+ end
+ end
+ end
+
def charset
connection.encoding
end
diff --git a/activerecord/test/cases/adapters/sqlite3/connection_test.rb b/activerecord/test/cases/adapters/sqlite3/connection_test.rb
new file mode 100644
index 0000000000..3dabc8766a
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/connection_test.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+class SQLite3ConnectionTest < ActiveRecord::SQLite3TestCase
+ fixtures :comments
+
+ def test_truncate
+ rows = ActiveRecord::Base.connection.exec_query("select count(*) from comments")
+ count = rows.first.values.first
+ assert_operator count, :>, 0
+
+ ActiveRecord::Base.connection.truncate("comments")
+ rows = ActiveRecord::Base.connection.exec_query("select count(*) from comments")
+ count = rows.first.values.first
+ assert_equal 0, count
+ end
+end
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 3fd1813d64..bd6fb099a4 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -2,6 +2,7 @@
require "cases/helper"
require "active_record/tasks/database_tasks"
+require "models/author"
module ActiveRecord
module DatabaseTasksSetupper
@@ -944,6 +945,127 @@ module ActiveRecord
end
end
+ unless in_memory_db?
+ class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ fixtures :authors, :author_addresses
+
+ def test_truncate_tables
+ assert_operator Author.count, :>, 0
+ assert_operator AuthorAddress.count, :>, 0
+
+ old_configurations = ActiveRecord::Base.configurations
+ configurations = { development: ActiveRecord::Base.configurations["arunit"] }
+ ActiveRecord::Base.configurations = configurations
+
+ ActiveRecord::Tasks::DatabaseTasks.stub(:root, nil) do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+
+ assert_equal 0, Author.count
+ assert_equal 0, AuthorAddress.count
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
+ end
+ end
+
+ class DatabaseTasksTruncateAllWithMultipleDatabasesTest < ActiveRecord::TestCase
+ def setup
+ @configurations = {
+ "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
+ "test" => { "primary" => { "database" => "test-db" }, "secondary" => { "database" => "secondary-test-db" } },
+ "production" => { "primary" => { "url" => "abstract://prod-db-host/prod-db" }, "secondary" => { "url" => "abstract://secondary-prod-db-host/secondary-prod-db" } }
+ }
+ end
+
+ def test_truncate_all_databases_for_environment
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :truncate_tables,
+ [
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all(
+ ActiveSupport::StringInquirer.new("test")
+ )
+ end
+ end
+ end
+
+ def test_truncate_all_databases_with_url_for_environment
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :truncate_tables,
+ [
+ ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"],
+ ["adapter" => "abstract", "database" => "secondary-prod-db", "host" => "secondary-prod-db-host"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+ end
+ end
+
+ def test_truncate_all_development_databases_when_env_was_no_specified
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :truncate_tables,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
+ end
+
+ def test_truncate_all_development_databases_when_env_is_development
+ old_env = ENV["RAILS_ENV"]
+ ENV["RAILS_ENV"] = "development"
+
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :truncate_tables,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.truncate_all(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
+ ensure
+ ENV["RAILS_ENV"] = old_env
+ end
+
+ private
+ def with_stubbed_configurations
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
+
+ yield
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
+ end
+
class DatabaseTasksCharsetTest < ActiveRecord::TestCase
include DatabaseTasksSetupper