diff options
Diffstat (limited to 'activerecord')
64 files changed, 495 insertions, 276 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 18c9a248b2..acdf7d40f8 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,11 +1,31 @@ +* Deprecate `initialize_schema_migrations_table` and `initialize_internal_metadata_table`. + + *Ryuta Kamizono* + +* Support foreign key creation for SQLite3. + + *Ryuta Kamizono* + +* Place generated migrations into the path set by `config.paths["db/migrate"]`. + + *Kevin Glowacz* + +* Raise `ActiveRecord::InvalidForeignKey` when a foreign key constraint fails on SQLite3. + + *Ryuta Kamizono* + +* Add the touch option to ActiveRecord#increment! and decrement!. + + *Hiroaki Izu* + * Deprecate passing a class to the `class_name` because it eagerloads more classes than necessary and potentially creates circular dependencies. *Kir Shatrov* -* Raise error when has_many through is defined before through association +* Raise error when has_many through is defined before through association. - Fixes #26834 + Fixes #26834. *Chris Holmes* @@ -416,7 +436,7 @@ *Ryuta Kamizono* -* Sqlite3 migrations to add a column to an existing table can now be +* SQLite3 migrations to add a column to an existing table can now be successfully rolled back when the column was given and invalid column type. diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index b624154def..8458253ff8 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -85,7 +85,7 @@ module ActiveRecord if target.persisted? && owner.persisted? && !target.save set_owner_attributes(target) - raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " + + raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " \ "The record failed to save after its foreign key was set to nil." end end diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 4cd1e64c3d..87e0847ec1 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -32,7 +32,7 @@ module ActiveRecord @alias_cache[node][column] end - class Table < Struct.new(:node, :columns) # :nodoc: + Table = Struct.new(:node, :columns) do # :nodoc: def table Arel::Nodes::TableAlias.new node.table, node.aliased_table_name end @@ -171,7 +171,7 @@ module ActiveRecord chain = child.reflection.chain foreign_table = parent.table foreign_klass = parent.base_klass - child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain) + child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, chain) end def make_outer_joins(parent, child) diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index a5705951f3..f5fcba1236 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -23,14 +23,11 @@ module ActiveRecord JoinInformation = Struct.new :joins, :binds - def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain) + def join_constraints(foreign_table, foreign_klass, node, join_type, tables, chain) joins = [] binds = [] tables = tables.reverse - scope_chain_index = 0 - scope_chain = scope_chain.reverse - # The chain starts with the target table, but we want to end with it here (makes # more sense in this context), so we reverse chain.reverse_each do |reflection| @@ -44,7 +41,7 @@ module ActiveRecord constraint = build_constraint(klass, table, key, foreign_table, foreign_key) predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table)) - scope_chain_items = scope_chain[scope_chain_index].map do |item| + scope_chain_items = reflection.scopes.map do |item| if item.is_a?(Relation) item else @@ -52,7 +49,6 @@ module ActiveRecord .instance_exec(node, &item) end end - scope_chain_index += 1 klass_scope = if klass.current_scope diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 945192fe04..4d9aff76cc 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -54,7 +54,7 @@ module ActiveRecord elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) } class_name_or_coder else - Coders::YAMLColumn.new(class_name_or_coder) + Coders::YAMLColumn.new(attr_name, class_name_or_coder) end decorate_attribute_type(attr_name, :serialize) do |type| diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb index 3a04a10fc9..2136da43fe 100644 --- a/activerecord/lib/active_record/coders/yaml_column.rb +++ b/activerecord/lib/active_record/coders/yaml_column.rb @@ -5,7 +5,8 @@ module ActiveRecord class YAMLColumn # :nodoc: attr_accessor :object_class - def initialize(object_class = Object) + def initialize(attr_name, object_class = Object) + @attr_name = attr_name @object_class = object_class check_arity_of_constructor end @@ -31,18 +32,16 @@ module ActiveRecord def assert_valid_value(obj) unless obj.nil? || obj.is_a?(object_class) raise SerializationTypeMismatch, - "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}" + "Attribute `#{@attr_name}` was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}" end end private def check_arity_of_constructor - begin - load(nil) - rescue ArgumentError - raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor." - end + load(nil) + rescue ArgumentError + raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor." end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 5ec2fc073e..ce4721c99d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -967,7 +967,7 @@ module ActiveRecord end def pool_from_any_process_for(spec_name) - owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] } + owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] } owner_to_pool && owner_to_pool[spec_name] end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 0c6bc16e6f..437e7c6510 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -1,4 +1,5 @@ require "active_support/core_ext/big_decimal/conversions" +require "active_support/multibyte/chars" module ActiveRecord module ConnectionAdapters # :nodoc: 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 322684672f..807df2184a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -15,9 +15,9 @@ module ActiveRecord end delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql, - :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options, to: :@conn + :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql, - :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options + :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options private @@ -49,7 +49,7 @@ module ActiveRecord statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) }) end - if supports_foreign_keys? + if supports_foreign_keys_in_create? statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) }) end 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 9b324c090b..b518ef760b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -3,29 +3,25 @@ module ActiveRecord # Abstract representation of an index definition on a table. Instances of # this type are typically created and returned by methods in database # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes - class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc: - end + IndexDefinition = Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc: # Abstract representation of a column definition. Instances of this type # are typically created by methods in TableDefinition, and added to the # +columns+ attribute of said TableDefinition object, in order to be used # for generating a number of table creation or table changing SQL statements. - class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type, :comment) #:nodoc: + ColumnDefinition = Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type, :comment) do #:nodoc: def primary_key? primary_key || type.to_sym == :primary_key end end - class AddColumnDefinition < Struct.new(:column) # :nodoc: - end + AddColumnDefinition = Struct.new(:column) # :nodoc: - class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc: - end + ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc: - class PrimaryKeyDefinition < Struct.new(:name) # :nodoc: - end + PrimaryKeyDefinition = Struct.new(:name) # :nodoc: - class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc: + ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc: def name options[:name] end 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 1bdc086380..d7e2818e35 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1008,15 +1008,15 @@ module ActiveRecord end end - # Should not be called normally, but this operation is non-destructive. - # The migrations module handles this automatically. - def initialize_schema_migrations_table + def initialize_schema_migrations_table # :nodoc: ActiveRecord::SchemaMigration.create_table end + deprecate :initialize_schema_migrations_table - def initialize_internal_metadata_table + def initialize_internal_metadata_table # :nodoc: ActiveRecord::InternalMetadata.create_table end + deprecate :initialize_internal_metadata_table def internal_string_options_for_primary_key # :nodoc: { primary_key: true } diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4046b3829d..348396de72 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -176,7 +176,7 @@ module ActiveRecord if @owner == Thread.current msg << "it is already leased by the current thread." else - msg << "it is already in use by a different thread: #{@owner}. " << + msg << "it is already in use by a different thread: #{@owner}. " \ "Current thread: #{Thread.current}." end raise ActiveRecordError, msg @@ -194,8 +194,8 @@ module ActiveRecord def expire if in_use? if @owner != Thread.current - raise ActiveRecordError, "Cannot expire connection, " << - "it is owned by a different thread: #{@owner}. " << + raise ActiveRecordError, "Cannot expire connection, " \ + "it is owned by a different thread: #{@owner}. " \ "Current thread: #{Thread.current}." end @@ -310,6 +310,12 @@ module ActiveRecord false end + # Does this adapter support creating foreign key constraints + # in the same statement as creating the table? + def supports_foreign_keys_in_create? + supports_foreign_keys? + end + # Does this adapter support views? def supports_views? false diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb index 0cf40de70f..f1ba0cb708 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb @@ -3,10 +3,8 @@ module ActiveRecord module MySQL module ColumnMethods def primary_key(name, type = :primary_key, **options) - if type == :primary_key && !options.key?(:default) - options[:auto_increment] = true - options[:limit] = 8 - end + options[:auto_increment] = true if [:primary_key, :integer, :bigint].include?(type) && !options.key?(:default) + options[:limit] = 8 if [:primary_key].include?(type) super end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb index 2065816501..d44c35714f 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb @@ -4,8 +4,8 @@ module ActiveRecord module ColumnDumper def column_spec_for_primary_key(column) spec = super - if column.type == :integer && !column.auto_increment? - spec[:default] = schema_default(column) || "nil" + if [:integer, :bigint].include?(schema_type(column)) && !column.auto_increment? + spec[:default] ||= schema_default(column) || "nil" end spec[:unsigned] = "true" if column.unsigned? spec diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index 4afb4733eb..c3e182b43d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -42,6 +42,7 @@ module ActiveRecord # a record (as primary keys cannot be +nil+). This might be done via the # +SecureRandom.uuid+ method and a +before_save+ callback, for instance. def primary_key(name, type = :primary_key, **options) + options[:auto_increment] = true if [:primary_key, :integer, :bigint].include?(type) && !options.key?(:default) if type == :uuid options[:default] = options.fetch(:default, "gen_random_uuid()") elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 0ebd907cc0..315d70c33f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -759,11 +759,11 @@ module ActiveRecord query(<<-end_sql, "SCHEMA") SELECT a.attname, format_type(a.atttypid, a.atttypmod), pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod, - (SELECT c.collname FROM pg_collation c, pg_type t - WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation), - col_description(a.attrelid, a.attnum) AS comment - FROM pg_attribute a LEFT JOIN pg_attrdef d - ON a.attrelid = d.adrelid AND a.attnum = d.adnum + c.collname, col_description(a.attrelid, a.attnum) AS comment + FROM pg_attribute a + LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum + LEFT JOIN pg_type t ON a.atttypid = t.oid + LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb index d0b38dff4c..f9bb7e6d82 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb @@ -3,7 +3,7 @@ module ActiveRecord module SQLite3 module ColumnMethods def primary_key(name, type = :primary_key, **options) - if options.delete(:auto_increment) == true && %i(integer bigint).include?(type) + if %i(integer bigint).include?(type) && (options.delete(:auto_increment) == true || !options.key?(:default)) type = :primary_key end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index ec44d020c2..ca6de37a6b 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -95,6 +95,8 @@ module ActiveRecord @active = nil @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit])) + + configure_connection end def supports_ddl_transactions? @@ -128,6 +130,10 @@ module ActiveRecord true end + def supports_foreign_keys_in_create? + sqlite_version >= "3.6.19" + end + def supports_views? true end @@ -185,6 +191,19 @@ module ActiveRecord true end + # REFERENTIAL INTEGRITY ==================================== + + def disable_referential_integrity # :nodoc: + old = select_value("PRAGMA foreign_keys") + + begin + execute("PRAGMA foreign_keys = OFF") + yield + ensure + execute("PRAGMA foreign_keys = #{old}") + end + end + #-- # DATABASE STATEMENTS ====================================== #++ @@ -424,6 +443,19 @@ module ActiveRecord rename_column_indexes(table_name, column.name, new_column_name) end + def foreign_keys(table_name) + fk_info = select_all("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA") + fk_info.map do |row| + options = { + column: row["from"], + primary_key: row["to"], + on_delete: extract_foreign_key_action(row["on_delete"]), + on_update: extract_foreign_key_action(row["on_update"]) + } + ForeignKeyDefinition.new(table_name, row["table"], options) + end + end + private def table_structure(table_name) @@ -525,6 +557,8 @@ module ActiveRecord RecordNotUnique.new(message) when /.* may not be NULL/, /NOT NULL constraint failed: .*/ NotNullViolation.new(message) + when /FOREIGN KEY constraint failed/i + InvalidForeignKey.new(message) else super end @@ -574,6 +608,18 @@ module ActiveRecord def create_table_definition(*args) SQLite3::TableDefinition.new(*args) end + + def extract_foreign_key_action(specifier) + case specifier + when "CASCADE"; :cascade + when "SET NULL"; :nullify + when "RESTRICT"; :restrict + end + end + + def configure_connection + execute("PRAGMA foreign_keys = ON", "SCHEMA") + end end end end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 4d30bdf196..0028dc0edb 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -308,7 +308,7 @@ module ActiveRecord relation = Relation.create(self, arel_table, predicate_builder) if finder_needs_type_condition? && !ignore_default_scope? - relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name) + relation.where(type_condition).create_with(inheritance_column.to_s => sti_name) else relation end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 93b9371206..cbd71a3779 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -105,7 +105,8 @@ module ActiveRecord end if touch - updates << sanitize_sql_for_assignment(touch_updates(touch)) + touch_updates = touch_updates(touch) + updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty? end unscoped.where(primary_key => id).update_all updates.join(", ") diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 6e5f5fa2a7..40f6226315 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -525,7 +525,7 @@ module ActiveRecord raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \ "Please specify the Rails release the migration was written for:\n" \ "\n" \ - " class #{self.class.name} < ActiveRecord::Migration[4.2]" + " class #{subclass} < ActiveRecord::Migration[4.2]" end end @@ -692,7 +692,7 @@ module ActiveRecord connection.respond_to?(:reverting) && connection.reverting end - class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc: + ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc: def up yield unless reverting end @@ -938,7 +938,7 @@ module ActiveRecord # MigrationProxy is used to defer loading of the actual migration classes # until they are needed - class MigrationProxy < Struct.new(:name, :version, :filename, :scope) + MigrationProxy = Struct.new(:name, :version, :filename, :scope) do def initialize(name, version, filename, scope) super @migration = nil @@ -1107,8 +1107,8 @@ module ActiveRecord validate(@migrations) - Base.connection.initialize_schema_migrations_table - Base.connection.initialize_internal_metadata_table + ActiveRecord::SchemaMigration.create_table + ActiveRecord::InternalMetadata.create_table end def current_version diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb index 2904634eb7..ffeca2c91e 100644 --- a/activerecord/lib/active_record/migration/compatibility.rb +++ b/activerecord/lib/active_record/migration/compatibility.rb @@ -16,7 +16,7 @@ module ActiveRecord class V5_0 < V5_1 def create_table(table_name, options = {}) if adapter_name == "PostgreSQL" - if options[:id] == :uuid && !options[:default] + if options[:id] == :uuid && !options.key?(:default) options[:default] = "uuid_generate_v4()" end end @@ -24,9 +24,8 @@ module ActiveRecord # Since 5.1 Postgres adapter uses bigserial type for primary # keys by default and MySQL uses bigint. This compat layer makes old migrations utilize # serial/int type instead -- the way it used to work before 5.1. - if options[:id].blank? + unless options.key?(:id) options[:id] = :integer - options[:auto_increment] = true end super diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 19fe9632ca..7ceb7d1a55 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -148,6 +148,8 @@ module ActiveRecord # # Attributes marked as readonly are silently ignored if the record is # being updated. + # + # Unless an error is raised, returns true. def save!(*args) create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self)) end @@ -341,11 +343,13 @@ module ActiveRecord # Wrapper around #increment that writes the update to the database. # Only +attribute+ is updated; the record itself is not saved. # This means that any other modified attributes will still be dirty. - # Validations and callbacks are skipped. Returns +self+. - def increment!(attribute, by = 1) + # Validations and callbacks are skipped. Supports the `touch` option from + # +update_counters+, see that for more. + # Returns +self+. + def increment!(attribute, by = 1, touch: nil) increment(attribute, by) change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0) - self.class.update_counters(id, attribute => change) + self.class.update_counters(id, attribute => change, touch: touch) clear_attribute_change(attribute) # eww self end @@ -360,9 +364,11 @@ module ActiveRecord # Wrapper around #decrement that writes the update to the database. # Only +attribute+ is updated; the record itself is not saved. # This means that any other modified attributes will still be dirty. - # Validations and callbacks are skipped. Returns +self+. - def decrement!(attribute, by = 1) - increment!(attribute, -by) + # Validations and callbacks are skipped. Supports the `touch` option from + # +update_counters+, see that for more. + # Returns +self+. + def decrement!(attribute, by = 1, touch: nil) + increment!(attribute, -by, touch: touch) end # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8af0d06ecb..81ec4924b0 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,5 +1,6 @@ require "thread" require "active_support/core_ext/string/filters" +require "active_support/deprecation" module ActiveRecord # = Active Record Reflection @@ -175,8 +176,19 @@ module ActiveRecord JoinKeys.new(foreign_key, active_record_primary_key) end + # Returns a list of scopes that should be applied for this Reflection + # object when querying the database. + def scopes + scope ? [scope] : [] + end + + def scope_chain + chain.map(&:scopes) + end + deprecate :scope_chain + def constraints - scope_chain.flatten + chain.map(&:scopes).flatten end def counter_cache_column @@ -321,7 +333,7 @@ module ActiveRecord end end - # Holds all the meta-data about an aggregation as it was specified in the + # Holds all the metadata about an aggregation as it was specified in the # Active Record class. class AggregateReflection < MacroReflection #:nodoc: def mapping @@ -330,7 +342,7 @@ module ActiveRecord end end - # Holds all the meta-data about an association as it was specified in the + # Holds all the metadata about an association as it was specified in the # Active Record class. class AssociationReflection < MacroReflection #:nodoc: # Returns the target association's class. @@ -461,12 +473,6 @@ module ActiveRecord false end - # An array of arrays of scopes. Each item in the outside array corresponds to a reflection - # in the #chain. - def scope_chain - scope ? [[scope]] : [[]] - end - def has_scope? scope end @@ -709,7 +715,7 @@ module ActiveRecord end end - # Holds all the meta-data about a :through association as it was specified + # Holds all the metadata about a :through association as it was specified # in the Active Record class. class ThroughReflection < AbstractReflection #:nodoc: attr_reader :delegate_reflection @@ -796,45 +802,12 @@ module ActiveRecord through_reflection.clear_association_scope_cache end - # Consider the following example: - # - # class Person - # has_many :articles - # has_many :comment_tags, through: :articles - # end - # - # class Article - # has_many :comments - # has_many :comment_tags, through: :comments, source: :tags - # end - # - # class Comment - # has_many :tags - # end - # - # There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags, - # but only Comment.tags will be represented in the #chain. So this method creates an array - # of scopes corresponding to the chain. - def scope_chain - @scope_chain ||= begin - scope_chain = source_reflection.scope_chain.map(&:dup) - - # Add to it the scope from this reflection (if any) - scope_chain.first << scope if scope - - through_scope_chain = through_reflection.scope_chain.map(&:dup) - - if options[:source_type] - type = foreign_type - source_type = options[:source_type] - through_scope_chain.first << lambda { |object| - where(type => source_type) - } - end + def scopes + source_reflection.scopes + super + end - # Recursively fill out the rest of the array from the through reflection - scope_chain + through_scope_chain - end + def source_type_scope + through_reflection.klass.where(foreign_type => options[:source_type]) end def has_scope? @@ -1008,6 +981,15 @@ module ActiveRecord @previous_reflection = previous_reflection end + def scopes + scopes = @previous_reflection.scopes + if @previous_reflection.options[:source_type] + scopes + [@previous_reflection.source_type_scope] + else + scopes + end + end + def klass @reflection.klass end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 7a2bc9c8af..5104427339 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -48,7 +48,7 @@ module ActiveRecord instance_eval(&block) if info[:version].present? - initialize_schema_migrations_table + ActiveRecord::SchemaMigration.create_table connection.assume_migrated_upto_version(info[:version], migrations_paths) end diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index d4be20d999..006afe7495 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -78,7 +78,7 @@ module ActiveRecord module ClassMethods def store(store_attribute, options = {}) - serialize store_attribute, IndifferentCoder.new(options[:coder]) + serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder]) store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors end @@ -177,12 +177,12 @@ module ActiveRecord end class IndifferentCoder # :nodoc: - def initialize(coder_or_class_name) + def initialize(attr_name, coder_or_class_name) @coder = if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump) coder_or_class_name else - ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object) + ActiveRecord::Coders::YAMLColumn.new(attr_name, coder_or_class_name || Object) end end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index bdb5184599..82604a915f 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -267,8 +267,8 @@ module ActiveRecord if seed_loader seed_loader.load_seed else - raise "You tried to load seed data, but no seed loader is specified. Please specify seed " + - "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" + + raise "You tried to load seed data, but no seed loader is specified. Please specify seed " \ + "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" \ "Seed loader should respond to load_seed method" end end diff --git a/activerecord/lib/rails/generators/active_record/migration.rb b/activerecord/lib/rails/generators/active_record/migration.rb index 4263c11ffc..43075077b9 100644 --- a/activerecord/lib/rails/generators/active_record/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration.rb @@ -20,6 +20,14 @@ module ActiveRecord key_type = options[:primary_key_type] ", id: :#{key_type}" if key_type end + + def db_migrate_path + if defined?(Rails) && Rails.application + Rails.application.config.paths["db/migrate"].to_ary.first + else + "db/migrate" + end + end end end end diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb index 8511531af7..1f1c47499b 100644 --- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb +++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb @@ -10,7 +10,7 @@ module ActiveRecord def create_migration_file set_local_assigns! validate_file_name! - migration_template @migration_template, "db/migrate/#{file_name}.rb" + migration_template @migration_template, File.join(db_migrate_path, "#{file_name}.rb") end # TODO Change this to private once we've dropped Ruby 2.2 support. diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb index a9df5212e3..5cec07d2e3 100644 --- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb +++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb @@ -17,7 +17,7 @@ module ActiveRecord def create_migration_file return unless options[:migration] && options[:parent].nil? attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false - migration_template "../../migration/templates/create_table_migration.rb", "db/migrate/create_#{table_name}.rb" + migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb") end def create_model_file diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 9828e682ef..a2b7d53205 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -31,7 +31,6 @@ module ActiveRecord end def test_tables - tables = nil tables = @connection.tables assert_includes tables, "accounts" assert_includes tables, "authors" @@ -185,34 +184,6 @@ module ActiveRecord end unless current_adapter?(:SQLite3Adapter) - def test_foreign_key_violations_are_translated_to_specific_exception - error = assert_raises(ActiveRecord::InvalidForeignKey) do - # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method - if @connection.prefetch_primary_key? - id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id")) - @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)" - else - @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)" - end - end - - assert_not_nil error.cause - end - - def test_foreign_key_violations_are_translated_to_specific_exception_with_validate_false - klass_has_fk = Class.new(ActiveRecord::Base) do - self.table_name = "fk_test_has_fk" - end - - error = assert_raises(ActiveRecord::InvalidForeignKey) do - has_fk = klass_has_fk.new - has_fk.fk_id = 1231231231 - has_fk.save(validate: false) - end - - assert_not_nil error.cause - end - def test_value_limit_violations_are_translated_to_specific_exception error = assert_raises(ActiveRecord::ValueTooLong) do Event.create(title: "abcdefgh") @@ -230,23 +201,6 @@ module ActiveRecord end end - def test_disable_referential_integrity - assert_nothing_raised do - @connection.disable_referential_integrity do - # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method - if @connection.prefetch_primary_key? - id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id")) - @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)" - else - @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)" - end - # should delete created record as otherwise disable_referential_integrity will try to enable constraints after executed block - # and will fail (at least on Oracle) - @connection.execute "DELETE FROM fk_test_has_fk" - end - end - end - def test_select_all_always_return_activerecord_result result = @connection.select_all "SELECT * FROM posts" assert result.is_a?(ActiveRecord::Result) @@ -290,6 +244,59 @@ module ActiveRecord end end + class AdapterForeignKeyTest < ActiveRecord::TestCase + self.use_transactional_tests = false + + def setup + @connection = ActiveRecord::Base.connection + end + + def test_foreign_key_violations_are_translated_to_specific_exception_with_validate_false + klass_has_fk = Class.new(ActiveRecord::Base) do + self.table_name = "fk_test_has_fk" + end + + error = assert_raises(ActiveRecord::InvalidForeignKey) do + has_fk = klass_has_fk.new + has_fk.fk_id = 1231231231 + has_fk.save(validate: false) + end + + assert_not_nil error.cause + end + + def test_foreign_key_violations_are_translated_to_specific_exception + error = assert_raises(ActiveRecord::InvalidForeignKey) do + insert_into_fk_test_has_fk + end + + assert_not_nil error.cause + end + + def test_disable_referential_integrity + assert_nothing_raised do + @connection.disable_referential_integrity do + insert_into_fk_test_has_fk + # should delete created record as otherwise disable_referential_integrity will try to enable constraints + # after executed block and will fail (at least on Oracle) + @connection.execute "DELETE FROM fk_test_has_fk" + end + end + end + + private + + def insert_into_fk_test_has_fk + # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method + if @connection.prefetch_primary_key? + id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id")) + @connection.execute "INSERT INTO fk_test_has_fk (id,fk_id) VALUES (#{id_value},0)" + else + @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)" + end + end + end + class AdapterTestWithoutTransaction < ActiveRecord::TestCase self.use_transactional_tests = false diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb index fa54aac6b3..605baa9905 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb @@ -16,7 +16,7 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase table_name = ActiveRecord::SchemaMigration.table_name connection.drop_table table_name, if_exists: true - connection.initialize_schema_migrations_table + ActiveRecord::SchemaMigration.create_table assert connection.column_exists?(table_name, :version, :string, collation: "utf8_general_ci") end @@ -27,7 +27,7 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase table_name = ActiveRecord::InternalMetadata.table_name connection.drop_table table_name, if_exists: true - connection.initialize_internal_metadata_table + ActiveRecord::InternalMetadata.create_table assert connection.column_exists?(table_name, :key, :string, collation: "utf8_general_ci") end diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 075301d6d5..3cbd4ca212 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -156,7 +156,7 @@ module ActiveRecord secondary_connection.query("select pg_terminate_backend(#{original_connection_pid.first.first})") ActiveRecord::Base.connection_pool.checkin(secondary_connection) elsif ARTest.config["with_manual_interventions"] - puts "Kill the connection now (e.g. by restarting the PostgreSQL " + + puts "Kill the connection now (e.g. by restarting the PostgreSQL " \ 'server with the "-m fast" option) and then press enter.' $stdin.gets else @@ -175,7 +175,7 @@ module ActiveRecord new_connection_pid = @connection.query("select pg_backend_pid()") assert_not_equal original_connection_pid, new_connection_pid, - "umm -- looks like you didn't break the connection, because we're still " + + "umm -- looks like you didn't break the connection, because we're still " \ "successfully querying with the same connection pid." # Repair all fixture connections so other tests won't break. diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb index 19b00258b6..b9e177e6ec 100644 --- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb +++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb @@ -30,7 +30,7 @@ class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase record = PostgresqlInfinity.new(float: "-Infinity") assert_equal(-Float::INFINITY, record.float) record = PostgresqlInfinity.new(float: "NaN") - assert_send [record.float, :nan?] + assert record.float.nan?, "Expected #{record.float} to be NaN" end test "update_all with infinity on a float column" do diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb index 834354dcc9..bfb2b7c27a 100644 --- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb +++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb @@ -31,7 +31,7 @@ class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase assert_equal 123456.789, first.double assert_equal(-::Float::INFINITY, second.single) assert_equal ::Float::INFINITY, second.double - assert_send [third.double, :nan?] + assert third.double.nan?, "Expected #{third.double} to be NaN" end def test_update diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index f34d50e25c..4655cd1d20 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -234,25 +234,23 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase end end - if ActiveRecord::Base.connection.supports_pgcrypto_uuid? - def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration - @verbose_was = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - - migration = Class.new(ActiveRecord::Migration[4.2]) do - def version; 101 end - def migrate(x) - create_table("pg_uuids_4", id: :uuid) - end - end.new - ActiveRecord::Migrator.new(:up, [migration]).migrate - - schema = dump_table_schema "pg_uuids_4" - assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) - ensure - drop_table "pg_uuids_4" - ActiveRecord::Migration.verbose = @verbose_was - end + def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid) + end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate + + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was end end end @@ -285,6 +283,25 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase schema = dump_table_schema "pg_uuids" assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema) end + + def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid, default: nil) + end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate + + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was + end end end diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb index 91967c1e33..e1cfd703e8 100644 --- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -41,8 +41,8 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase test_copy_table("comments", "comments_with_index") do @connection.add_index("comments_with_index", ["post_id", "type"]) test_copy_table("comments_with_index", "comments_with_index2") do - assert_equal table_indexes_without_name("comments_with_index"), - table_indexes_without_name("comments_with_index2") + assert_nil table_indexes_without_name("comments_with_index") + assert_nil table_indexes_without_name("comments_with_index2") end end end @@ -59,7 +59,8 @@ class CopyTableTest < ActiveRecord::SQLite3TestCase copied_id = @connection.columns("goofy_string_id2").detect { |col| col.name == "id" } assert_equal original_id.type, copied_id.type assert_equal original_id.sql_type, copied_id.sql_type - assert_equal original_id.limit, copied_id.limit + assert_nil original_id.limit + assert_nil copied_id.limit end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index ff1bf8acd4..11f4aae5b3 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -739,18 +739,25 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_invalid_association_reference - assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") { + e = assert_raise(ActiveRecord::AssociationNotFoundError) { Post.all.merge!(includes: :monkeys).find(6) } - assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") { + assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message) + + e = assert_raise(ActiveRecord::AssociationNotFoundError) { Post.all.merge!(includes: [ :monkeys ]).find(6) } - assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") { + assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message) + + e = assert_raise(ActiveRecord::AssociationNotFoundError) { Post.all.merge!(includes: [ "monkeys" ]).find(6) } - assert_raise(ActiveRecord::AssociationNotFoundError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") { + assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message) + + e = assert_raise(ActiveRecord::AssociationNotFoundError) { Post.all.merge!(includes: [ :monkeys, :elephants ]).find(6) } + assert_equal("Association named 'monkeys' was not found on Post; perhaps you misspelled it?", e.message) end def test_eager_has_many_through_with_order @@ -933,7 +940,11 @@ class EagerAssociationTest < ActiveRecord::TestCase d2 = find_all_ordered(Firm, :account) d1.each_index do |i| assert_equal(d1[i], d2[i]) - assert_equal(d1[i].account, d2[i].account) + if d1[i].account.nil? + assert_nil(d2[i].account) + else + assert_equal(d1[i].account, d2[i].account) + end end end @@ -943,7 +954,13 @@ class EagerAssociationTest < ActiveRecord::TestCase d2 = find_all_ordered(Client, firm_types) d1.each_index do |i| assert_equal(d1[i], d2[i]) - firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) } + firm_types.each do |type| + if (expected = d1[i].send(type)).nil? + assert_nil(d2[i].send(type)) + else + assert_equal(expected, d2[i].send(type)) + end + end end end def test_eager_with_valid_association_as_string_not_symbol diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index aa910ba409..fc1e61124c 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -476,7 +476,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_equal ships(:black_pearl), pirate.ship assert_equal pirate.id, pirate.ship.pirate_id - assert_equal "Failed to remove the existing associated ship. " + + assert_equal "Failed to remove the existing associated ship. " \ "The record failed to save after its foreign key was set to nil.", error.message end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 6fe6ee6783..287b3e9ebc 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -443,7 +443,7 @@ class InverseHasManyTests < ActiveRecord::TestCase assert man.equal?(man.interests.first.man), "Two inverses should lead back to the same object that was originally held" assert man.equal?(man.interests.find(interest.id).man), "Two inversions should lead back to the same object that was originally held" - assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match before the name is changed" + assert_nil man.interests.find(interest.id).man.name, "The name of the man should match before the name is changed" man.name = "Ben Bitdiddle" assert_equal man.name, man.interests.find(interest.id).man.name, "The name of the man should match after the parent name is changed" man.interests.find(interest.id).man.name = "Alyssa P. Hacker" diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index 978dd93fa4..1fc63a49d4 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -3,7 +3,7 @@ require "cases/helper" module ActiveRecord module AttributeMethods class ReadTest < ActiveRecord::TestCase - class FakeColumn < Struct.new(:name) + FakeColumn = Struct.new(:name) do def type; :integer; end end diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb index b9c6224425..1e0b4580af 100644 --- a/activerecord/test/cases/coders/yaml_column_test.rb +++ b/activerecord/test/cases/coders/yaml_column_test.rb @@ -5,46 +5,48 @@ module ActiveRecord module Coders class YAMLColumnTest < ActiveRecord::TestCase def test_initialize_takes_class - coder = YAMLColumn.new(Object) + coder = YAMLColumn.new("attr_name", Object) assert_equal Object, coder.object_class end def test_type_mismatch_on_different_classes_on_dump - coder = YAMLColumn.new(Array) - assert_raises(SerializationTypeMismatch) do + coder = YAMLColumn.new("attr_name", Array) + error = assert_raises(SerializationTypeMismatch) do coder.dump("a") end + assert_equal %{Attribute `attr_name` was supposed to be a Array, but was a String. -- "a"}, error.to_s end def test_type_mismatch_on_different_classes - coder = YAMLColumn.new(Array) - assert_raises(SerializationTypeMismatch) do + coder = YAMLColumn.new("attr_name", Array) + error = assert_raises(SerializationTypeMismatch) do coder.load "--- foo" end + assert_equal %{Attribute `attr_name` was supposed to be a Array, but was a String. -- "foo"}, error.to_s end def test_nil_is_ok - coder = YAMLColumn.new + coder = YAMLColumn.new("attr_name") assert_nil coder.load "--- " end def test_returns_new_with_different_class - coder = YAMLColumn.new SerializationTypeMismatch + coder = YAMLColumn.new("attr_name", SerializationTypeMismatch) assert_equal SerializationTypeMismatch, coder.load("--- ").class end def test_returns_string_unless_starts_with_dash - coder = YAMLColumn.new + coder = YAMLColumn.new("attr_name") assert_equal "foo", coder.load("foo") end def test_load_handles_other_classes - coder = YAMLColumn.new + coder = YAMLColumn.new("attr_name") assert_equal [], coder.load([]) end def test_load_doesnt_swallow_yaml_exceptions - coder = YAMLColumn.new + coder = YAMLColumn.new("attr_name") bad_yaml = "--- {" assert_raises(Psych::SyntaxError) do coder.load(bad_yaml) @@ -52,7 +54,7 @@ module ActiveRecord end def test_load_doesnt_handle_undefined_class_or_module - coder = YAMLColumn.new + coder = YAMLColumn.new("attr_name") missing_class_yaml = '--- !ruby/object:DoesNotExistAndShouldntEver {}\n' assert_raises(ArgumentError) do coder.load(missing_class_yaml) diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 2c33bf22ab..4f2392042b 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -89,6 +89,41 @@ module ActiveRecord rd.close end + def test_pool_from_any_process_for_uses_most_recent_spec + skip unless current_adapter?(:SQLite3Adapter) + + file = Tempfile.new "lol.sqlite3" + + rd, wr = IO.pipe + rd.binmode + wr.binmode + + pid = fork do + ActiveRecord::Base.configurations["arunit"]["database"] = file.path + ActiveRecord::Base.establish_connection(:arunit) + + pid2 = fork do + wr.write ActiveRecord::Base.connection_config[:database] + wr.close + end + + Process.waitpid pid2 + end + + Process.waitpid pid + + wr.close + + assert_equal file.path, rd.read + + rd.close + ensure + if file + file.close + file.unlink + end + end + def test_a_class_using_custom_pool_and_switching_back_to_primary klass2 = Class.new(Base) { def self.name; "klass2"; end } diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 81d7113bc6..9f7280634e 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -399,7 +399,7 @@ module ActiveRecord all_threads_in_new_connection.wait end rescue Timeout::Error - flunk "pool unable to establish connections concurrently or implementation has " << + flunk "pool unable to establish connections concurrently or implementation has " \ "changed, this test then needs to patch a different :new_connection method" ensure # clean up the threads diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index c7d0ba32b4..46d7526cc0 100644 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -221,6 +221,15 @@ class CounterCacheTest < ActiveRecord::TestCase assert_equal previously_updated_at, @topic.updated_at end + test "update counters doesn't touch timestamps with touch: []" do + @topic.update_column :updated_at, 5.minutes.ago + previously_updated_at = @topic.updated_at + + Topic.update_counters(@topic.id, replies_count: -1, touch: []) + + assert_equal previously_updated_at, @topic.updated_at + end + test "update counters with touch: true" do assert_touching @topic, :updated_at do Topic.update_counters(@topic.id, replies_count: -1, touch: true) diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb index a1c3c5af9c..e4a2f9ee17 100644 --- a/activerecord/test/cases/date_time_precision_test.rb +++ b/activerecord/test/cases/date_time_precision_test.rb @@ -73,7 +73,7 @@ if subsecond_precision_supported? assert_match %r{t\.datetime\s+"updated_at",\s+precision: 6,\s+null: false$}, output end - if current_adapter?(:PostgreSQLAdapter) + if current_adapter?(:PostgreSQLAdapter, :SQLServerAdapter) def test_datetime_precision_with_zero_should_be_dumped @connection.create_table(:foos, force: true) do |t| t.timestamps precision: 0 diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index e5a7412bc3..9296f3da90 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -103,9 +103,10 @@ module ActiveRecord end def test_legacy_migrations_raises_exception_when_inherited - assert_raises(StandardError) do - Class.new(ActiveRecord::Migration) + e = assert_raises(StandardError) do + class_eval("class LegacyMigration < ActiveRecord::Migration; end") end + assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message) end end end diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 9be6667aa1..96e775a58b 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -2,6 +2,26 @@ require "cases/helper" require "support/ddl_helper" require "support/schema_dumping_helper" +if ActiveRecord::Base.connection.supports_foreign_keys_in_create? + module ActiveRecord + class Migration + class ForeignKeyInCreateTest < ActiveRecord::TestCase + def test_foreign_keys + foreign_keys = ActiveRecord::Base.connection.foreign_keys("fk_test_has_fk") + assert_equal 1, foreign_keys.size + + fk = foreign_keys.first + assert_equal "fk_test_has_fk", fk.from_table + assert_equal "fk_test_has_pk", fk.to_table + assert_equal "fk_id", fk.column + assert_equal "pk_id", fk.primary_key + assert_equal "fk_name", fk.name unless current_adapter?(:SQLite3Adapter) + end + end + end + end +end + if ActiveRecord::Base.connection.supports_foreign_keys? module ActiveRecord class Migration @@ -29,10 +49,8 @@ if ActiveRecord::Base.connection.supports_foreign_keys? end teardown do - if defined?(@connection) - @connection.drop_table "astronauts", if_exists: true - @connection.drop_table "rockets", if_exists: true - end + @connection.drop_table "astronauts", if_exists: true + @connection.drop_table "rockets", if_exists: true end def test_foreign_keys @@ -305,9 +323,11 @@ else @connection.remove_foreign_key :clubs, :categories end - def test_foreign_keys_should_raise_not_implemented - assert_raises NotImplementedError do - @connection.foreign_keys("clubs") + unless current_adapter?(:SQLite3Adapter) + def test_foreign_keys_should_raise_not_implemented + assert_raises NotImplementedError do + @connection.foreign_keys("clubs") + end end end end diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb index 4957ab8b3d..560adcbfed 100644 --- a/activerecord/test/cases/migration/references_foreign_key_test.rb +++ b/activerecord/test/cases/migration/references_foreign_key_test.rb @@ -1,9 +1,9 @@ require "cases/helper" -if ActiveRecord::Base.connection.supports_foreign_keys? +if ActiveRecord::Base.connection.supports_foreign_keys_in_create? module ActiveRecord class Migration - class ReferencesForeignKeyTest < ActiveRecord::TestCase + class ReferencesForeignKeyInCreateTest < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.connection @connection.create_table(:testing_parents, force: true) @@ -61,6 +61,24 @@ if ActiveRecord::Base.connection.supports_foreign_keys? assert_equal([["testings", "testing_parents", "parent_id"]], fks.map { |fk| [fk.from_table, fk.to_table, fk.column] }) end + end + end + end +end + +if ActiveRecord::Base.connection.supports_foreign_keys? + module ActiveRecord + class Migration + class ReferencesForeignKeyTest < ActiveRecord::TestCase + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table(:testing_parents, force: true) + end + + teardown do + @connection.drop_table "testings", if_exists: true + @connection.drop_table "testing_parents", if_exists: true + end test "foreign keys cannot be added to polymorphic relations when creating the table" do @connection.create_table :testings do |t| diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 4edb807bbb..2fda4d60b8 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -43,10 +43,10 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::Base.table_name_prefix = "" ActiveRecord::Base.table_name_suffix = "" - ActiveRecord::Base.connection.initialize_schema_migrations_table - ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}" + ActiveRecord::SchemaMigration.create_table + ActiveRecord::SchemaMigration.delete_all - %w(things awesome_things prefix_things_suffix p_awesome_things_s ).each do |table| + %w(things awesome_things prefix_things_suffix p_awesome_things_s).each do |table| Thing.connection.drop_table(table) rescue nil end Thing.reset_column_information @@ -916,7 +916,6 @@ if ActiveRecord::Base.connection.supports_bulk_alter? @indexes ||= Person.connection.indexes("delete_me") end end # AlterTableMigrationsTest - end class CopyMigrationsTest < ActiveRecord::TestCase @@ -1134,4 +1133,9 @@ class CopyMigrationsTest < ActiveRecord::TestCase def test_unknown_migration_version_should_raise_an_argument_error assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] } end + + def test_deprecate_initialize_internal_tables + assert_deprecated { ActiveRecord::Base.connection.initialize_schema_migrations_table } + assert_deprecated { ActiveRecord::Base.connection.initialize_internal_metadata_table } + end end diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index 224e8d1854..20d70b75ac 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -45,10 +45,11 @@ class MigratorTest < ActiveRecord::TestCase end def test_migrator_with_duplicate_names - assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do + e = assert_raises(ActiveRecord::DuplicateMigrationNameError) do list = [ActiveRecord::Migration.new("Chunky"), ActiveRecord::Migration.new("Chunky")] ActiveRecord::Migrator.new(:up, list) end + assert_match(/Multiple migrations have the name Chunky/, e.message) end def test_migrator_with_duplicate_versions diff --git a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb index 350a966d40..b9d2acbed2 100644 --- a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb +++ b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb @@ -120,14 +120,14 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase assert_assignment_affects_records_in_target(:birds_with_add) end - test("Assignment updates records in target when not loaded" + + test("Assignment updates records in target when not loaded" \ " and callback loads target") do assert_not @pirate.birds_with_add_load.loaded? @pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes assert_assignment_affects_records_in_target(:birds_with_add_load) end - test("Assignment updates records in target when loaded" + + test("Assignment updates records in target when loaded" \ " and callback loads target") do @pirate.birds_with_add_load.load_target @pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 3f1da82cb4..5b7e2fd008 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -139,6 +139,14 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal initial_credit + 2, a1.reload.credit_limit end + def test_increment_updates_timestamps + topic = topics(:first) + topic.update_columns(updated_at: 5.minutes.ago) + previous_updated_at = topic.updated_at + topic.increment!(:replies_count, touch: true) + assert_operator previous_updated_at, :<, topic.reload.updated_at + end + def test_destroy_all conditions = "author_name = 'Mary'" topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a @@ -230,6 +238,14 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal 41, accounts(:signals37, :reload).credit_limit end + def test_decrement_updates_timestamps + topic = topics(:first) + topic.update_columns(updated_at: 5.minutes.ago) + previous_updated_at = topic.updated_at + topic.decrement!(:replies_count, touch: true) + assert_operator previous_updated_at, :<, topic.reload.updated_at + end + def test_create topic = Topic.new topic.title = "New Topic" diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 93a67d0738..04e48e201e 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -442,6 +442,10 @@ end class QueryCacheExpiryTest < ActiveRecord::TestCase fixtures :tasks, :posts, :categories, :categories_posts + def teardown + Task.connection.clear_query_cache + end + def test_cache_gets_cleared_after_migration # warm the cache Post.find(1) diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 2444eccab1..04ee67c177 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -258,7 +258,9 @@ class ReflectionTest < ActiveRecord::TestCase [Post.reflect_on_association(:first_taggings).scope], [Author.reflect_on_association(:misc_posts).scope] ] - actual = Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain + actual = assert_deprecated do + Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain + end assert_equal expected, actual expected = [ @@ -270,7 +272,9 @@ class ReflectionTest < ActiveRecord::TestCase [], [] ] - actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain + actual = assert_deprecated do + Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain + end assert_equal expected, actual end @@ -395,9 +399,15 @@ class ReflectionTest < ActiveRecord::TestCase end def test_through_reflection_scope_chain_does_not_modify_other_reflections - orig_conds = Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect - Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain - assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).scope_chain.inspect + orig_conds = assert_deprecated do + Post.reflect_on_association(:first_blue_tags_2).scope_chain + end.inspect + assert_deprecated do + Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain + end + assert_equal orig_conds, assert_deprecated { + Post.reflect_on_association(:first_blue_tags_2).scope_chain + }.inspect end def test_symbol_for_class_name diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb index de15eb9187..4f92f71a09 100644 --- a/activerecord/test/cases/relation/mutation_test.rb +++ b/activerecord/test/cases/relation/mutation_test.rb @@ -3,7 +3,7 @@ require "models/post" module ActiveRecord class RelationMutationTest < ActiveRecord::TestCase - class FakeKlass < Struct.new(:table_name, :name) + FakeKlass = Struct.new(:table_name, :name) do extend ActiveRecord::Delegation::DelegateCache inherited self diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index d5af0cc9a5..1241bb54a4 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -8,7 +8,7 @@ module ActiveRecord class RelationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors - class FakeKlass < Struct.new(:table_name, :name) + FakeKlass = Struct.new(:table_name, :name) do extend ActiveRecord::Delegation::DelegateCache inherited self diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index bea78d2a95..34c5f356b8 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -343,7 +343,7 @@ class SchemaDumperTest < ActiveRecord::TestCase t.column :name, :string t.column :owner_id, :bigint t.index [:name] - t.foreign_key :dog_owners, column: "owner_id" if supports_foreign_keys? + t.foreign_key :dog_owners, column: "owner_id" end end def down diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index a469da0a5b..24b83691f6 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -240,6 +240,20 @@ class SerializedAttributeTest < ActiveRecord::TestCase assert_equal [], light.long_state end + def test_unexpected_serialized_type + Topic.serialize :content, Hash + topic = Topic.create!(content: { zomg: true }) + + Topic.serialize :content, Array + + topic.reload + error = assert_raise(ActiveRecord::SerializationTypeMismatch) do + topic.content + end + expected = "Attribute `content` was supposed to be a Array, but was a Hash. -- {:zomg=>true}" + assert_equal expected, error.to_s + end + def test_serialized_column_should_unserialize_after_update_column t = Topic.create(content: "first") assert_equal("first", t.content) diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb index 03f6c234e8..09c585167e 100644 --- a/activerecord/test/cases/time_precision_test.rb +++ b/activerecord/test/cases/time_precision_test.rb @@ -68,7 +68,7 @@ if subsecond_precision_supported? assert_match %r{t\.time\s+"finish",\s+precision: 6$}, output end - if current_adapter?(:PostgreSQLAdapter) + if current_adapter?(:PostgreSQLAdapter, :SQLServerAdapter) def test_time_precision_with_zero_should_be_dumped @connection.create_table(:foos, force: true) do |t| t.time :start, precision: 0 diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 9b1cca8583..8f9980f168 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -279,7 +279,11 @@ class TransactionTest < ActiveRecord::TestCase e = assert_raises(RuntimeError) { new_topic.save } assert_equal "Make the transaction rollback", e.message assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value" - assert_equal id_snapshot, new_topic.id, "The topic should have its old id" + if id_snapshot.nil? + assert_nil new_topic.id, "The topic should have its old id" + else + assert_equal id_snapshot, new_topic.id, "The topic should have its old id" + end assert_equal id_present, new_topic.has_attribute?(Topic.primary_key) end end diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index d055968a56..07288568e8 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -154,7 +154,7 @@ if ActiveRecord::Base.connection.supports_views? end # sqlite dose not support CREATE, INSERT, and DELETE for VIEW - if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) + if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter, :SQLServerAdapter) class UpdateableViewTest < ActiveRecord::TestCase self.use_transactional_tests = false fixtures :books @@ -200,7 +200,7 @@ if ActiveRecord::Base.connection.supports_views? end end end - end # end of `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)` + end # end of `if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter, :SQLServerAdapter)` end # end of `if ActiveRecord::Base.connection.supports_views?` if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) && diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index ba6f5de894..b38b9661b3 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -1005,16 +1005,14 @@ ActiveRecord::Schema.define do create_table :records, force: true do |t| end - if supports_foreign_keys? - # fk_test_has_fk should be before fk_test_has_pk - create_table :fk_test_has_fk, force: true do |t| - t.bigint :fk_id, null: false + disable_referential_integrity do + create_table :fk_test_has_pk, primary_key: "pk_id", force: :cascade do |t| end - create_table :fk_test_has_pk, force: true, primary_key: "pk_id" do |t| + create_table :fk_test_has_fk, force: true do |t| + t.references :fk, null: false + t.foreign_key :fk_test_has_pk, column: "fk_id", name: "fk_name", primary_key: "pk_id" end - - add_foreign_key :fk_test_has_fk, :fk_test_has_pk, column: "fk_id", name: "fk_name", primary_key: "pk_id" end create_table :overloaded_types, force: true do |t| diff --git a/activerecord/test/schema/sqlite_specific_schema.rb b/activerecord/test/schema/sqlite_specific_schema.rb deleted file mode 100644 index cc7c36fe2b..0000000000 --- a/activerecord/test/schema/sqlite_specific_schema.rb +++ /dev/null @@ -1,18 +0,0 @@ -ActiveRecord::Schema.define do - execute "DROP TABLE fk_test_has_fk" rescue nil - execute "DROP TABLE fk_test_has_pk" rescue nil - execute <<_SQL - CREATE TABLE 'fk_test_has_pk' ( - 'pk_id' INTEGER NOT NULL PRIMARY KEY - ); -_SQL - - execute <<_SQL - CREATE TABLE 'fk_test_has_fk' ( - 'id' INTEGER NOT NULL PRIMARY KEY, - 'fk_id' INTEGER NOT NULL, - - FOREIGN KEY ('fk_id') REFERENCES 'fk_test_has_pk'('pk_id') - ); -_SQL -end |