diff options
Diffstat (limited to 'activerecord')
7 files changed, 88 insertions, 27 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 6f08b1b8fe..09fdc66788 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,15 +1,20 @@ -* Fix invalid schema when primary key column has a comment +* Fix sqlite3 collation parsing when using decimal columns. - Fixes #29966 + *Martin R. Schuster* + +* Fix invalid schema when primary key column has a comment. + + Fixes #29966. *Guilherme Goettems Schneider* -* Fix table comment also being applied to the primary key column +* Fix table comment also being applied to the primary key column. *Guilherme Goettems Schneider* * Allow generated `create_table` migrations to include or skip timestamps. - *Michael Duchemin* + *Michael Duchemin* + Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 7f3f32162e..9fc5ee3ab4 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -477,9 +477,9 @@ module ActiveRecord result = exec_query(sql, "SCHEMA").first if result - # Splitting with left parentheses and picking up last will return all + # Splitting with left parentheses and discarding the first part will return all # columns separated with comma(,). - columns_string = result["sql"].split("(").last + columns_string = result["sql"].split("(", 2).last columns_string.split(",").each do |column_string| # This regex will match the column name and collation type and will save diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 040ebdb960..d472eb2e2b 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -173,7 +173,7 @@ module ActiveRecord raise "Anonymous class is not allowed." unless name config_or_env ||= DEFAULT_ENV.call.to_sym - pool_name = self == Base ? "primary" : name + pool_name = primary_class? ? "primary" : name self.connection_specification_name = pool_name resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations) @@ -204,11 +204,15 @@ module ActiveRecord # Return the specification name from the current class or its parent. def connection_specification_name if !defined?(@connection_specification_name) || @connection_specification_name.nil? - return self == Base ? "primary" : superclass.connection_specification_name + return primary_class? ? "primary" : superclass.connection_specification_name end @connection_specification_name end + def primary_class? # :nodoc: + self == Base || defined?(ApplicationRecord) && self == ApplicationRecord + end + # Returns the configuration of the associated connection as a hash: # # ActiveRecord::Base.connection_config diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index e0bc5180c0..befcbc8984 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -266,12 +266,31 @@ db_namespace = namespace :db do desc "Runs setup if database does not exist, or runs migrations if it does" task prepare: :load_config do + seed = false + ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config| ActiveRecord::Base.establish_connection(db_config.config) - db_namespace["migrate"].invoke + + ActiveRecord::Tasks::DatabaseTasks.migrate + + # Skipped when no database + ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name) + rescue ActiveRecord::NoDatabaseError - db_namespace["setup"].invoke + ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name) + ActiveRecord::Tasks::DatabaseTasks.load_schema( + db_config.config, + ActiveRecord::Base.schema_format, + nil, + db_config.env_name, + db_config.spec_name + ) + + seed = true end + + ActiveRecord::Base.establish_connection + ActiveRecord::Tasks::DatabaseTasks.load_seed if seed end desc "Loads the seed data from db/seeds.rb" @@ -336,13 +355,9 @@ db_namespace = namespace :db do namespace :schema do desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record" task dump: :load_config do - require "active_record/schema_dumper" ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config| - filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :ruby) - File.open(filename, "w:utf-8") do |file| - ActiveRecord::Base.establish_connection(db_config.config) - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) - end + ActiveRecord::Base.establish_connection(db_config.config) + ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :ruby, db_config.spec_name) end db_namespace["schema:dump"].reenable @@ -385,14 +400,7 @@ db_namespace = namespace :db do task dump: :load_config do ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config| ActiveRecord::Base.establish_connection(db_config.config) - filename = ActiveRecord::Tasks::DatabaseTasks.dump_filename(db_config.spec_name, :sql) - ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config.config, filename) - if ActiveRecord::SchemaMigration.table_exists? - File.open(filename, "a") do |f| - f.puts ActiveRecord::Base.connection.dump_schema_information - f.print "\n" - end - end + ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, :sql, db_config.spec_name) end db_namespace["structure:dump"].reenable diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index c79ed8db60..636db0f33d 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -169,8 +169,8 @@ module ActiveRecord end end - def create_current(environment = env) - each_current_configuration(environment) { |configuration| + def create_current(environment = env, spec_name = nil) + each_current_configuration(environment, spec_name) { |configuration| create configuration } ActiveRecord::Base.establish_connection(environment.to_sym) @@ -325,6 +325,26 @@ module ActiveRecord Migration.verbose = verbose_was end + def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") + require "active_record/schema_dumper" + filename = dump_filename(spec_name, format) + + case format + when :ruby + File.open(filename, "w:utf-8") do |file| + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) + end + when :sql + structure_dump(configuration, filename) + if ActiveRecord::SchemaMigration.table_exists? + File.open(filename, "a") do |f| + f.puts ActiveRecord::Base.connection.dump_schema_information + f.print "\n" + end + end + end + end + def schema_file(format = ActiveRecord::Base.schema_format) File.join(db_dir, schema_file_type(format)) end @@ -406,12 +426,14 @@ module ActiveRecord task.is_a?(String) ? task.constantize : task end - def each_current_configuration(environment) + def each_current_configuration(environment, spec_name = nil) environments = [environment] environments << "test" if environment == "development" environments.each do |env| ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config| + next if spec_name && spec_name != db_config.spec_name + yield db_config.config, db_config.spec_name, env end end diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb index 76c8f7d8dd..d938b5ff2f 100644 --- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb @@ -11,6 +11,10 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase @connection.create_table :collation_table_sqlite3, force: true do |t| t.string :string_nocase, collation: "NOCASE" t.text :text_rtrim, collation: "RTRIM" + # The decimal column might interfere with collation parsing. + # Thus, add this column type and some other string column afterwards. + t.decimal :decimal_col, precision: 6, scale: 2 + t.string :string_after_decimal_nocase, collation: "NOCASE" end end @@ -22,6 +26,11 @@ class SQLite3CollationTest < ActiveRecord::SQLite3TestCase column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_nocase" } assert_equal :string, column.type assert_equal "NOCASE", column.collation + + # Verify collation of a column behind the decimal column as well. + column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == "string_after_decimal_nocase" } + assert_equal :string, column.type + assert_equal "NOCASE", column.collation end test "text column with collation" do diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 6282759a10..27589966af 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -367,11 +367,24 @@ module ActiveRecord assert_same klass2.connection, ActiveRecord::Base.connection end + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + end + + class MyClass < ApplicationRecord + end + def test_connection_specification_name_should_fallback_to_parent klassA = Class.new(Base) klassB = Class.new(klassA) + klassC = Class.new(MyClass) assert_equal klassB.connection_specification_name, klassA.connection_specification_name + assert_equal klassC.connection_specification_name, klassA.connection_specification_name + + assert_equal "primary", klassA.connection_specification_name + assert_equal "primary", klassC.connection_specification_name + klassA.connection_specification_name = "readonly" assert_equal "readonly", klassB.connection_specification_name end |