aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md43
-rw-r--r--activerecord/Rakefile3
-rw-r--r--activerecord/lib/active_record/aggregations.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb14
-rw-r--r--activerecord/lib/active_record/base.rb4
-rw-r--r--activerecord/lib/active_record/collection_cache_key.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb51
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb104
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb70
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb18
-rw-r--r--activerecord/lib/active_record/core.rb5
-rw-r--r--activerecord/lib/active_record/enum.rb13
-rw-r--r--activerecord/lib/active_record/errors.rb5
-rw-r--r--activerecord/lib/active_record/gem_version.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb8
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb20
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb25
-rw-r--r--activerecord/lib/active_record/model_schema.rb4
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb14
-rw-r--r--activerecord/lib/active_record/persistence.rb21
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/reflection.rb84
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/relation/batches.rb6
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb12
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb25
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb10
-rw-r--r--activerecord/lib/active_record/schema_migration.rb17
-rw-r--r--activerecord/lib/active_record/scoping/default.rb2
-rw-r--r--activerecord/lib/active_record/scoping/named.rb10
-rw-r--r--activerecord/lib/active_record/table_metadata.rb6
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb5
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb31
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb6
-rw-r--r--activerecord/test/cases/ar_schema_test.rb2
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb9
-rw-r--r--activerecord/test/cases/base_test.rb2
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb11
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb6
-rw-r--r--activerecord/test/cases/defaults_test.rb3
-rw-r--r--activerecord/test/cases/enum_test.rb10
-rw-r--r--activerecord/test/cases/inheritance_test.rb4
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb48
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb4
-rw-r--r--activerecord/test/cases/migration/references_index_test.rb8
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb10
-rw-r--r--activerecord/test/cases/migration/table_and_index_test.rb24
-rw-r--r--activerecord/test/cases/migration_test.rb25
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb13
-rw-r--r--activerecord/test/cases/persistence_test.rb2
-rw-r--r--activerecord/test/cases/primary_keys_test.rb27
-rw-r--r--activerecord/test/cases/reflection_test.rb10
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb4
-rw-r--r--activerecord/test/cases/relation/record_fetch_warning_test.rb4
-rw-r--r--activerecord/test/cases/relations_test.rb34
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb28
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb19
-rw-r--r--activerecord/test/cases/store_test.rb2
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb26
-rw-r--r--activerecord/test/models/author.rb2
-rw-r--r--activerecord/test/models/book.rb1
-rw-r--r--activerecord/test/models/chef.rb4
-rw-r--r--activerecord/test/models/hotel.rb4
-rw-r--r--activerecord/test/models/mocktail_designer.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb11
-rw-r--r--activerecord/test/schema/schema.rb14
96 files changed, 815 insertions, 458 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 81799b65d6..0832652d32 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,39 @@
+* Bumped the minimum supported version of PostgreSQL to >= 9.1.
+ Both PG 9.0 and 8.4 are past their end of life date:
+ http://www.postgresql.org/support/versioning/
+
+ *Remo Mueller*
+
+## Rails 5.0.0.beta2 (February 01, 2016) ##
+
+* `ActiveRecord::Relation#reverse_order` throws `ActiveRecord::IrreversibleOrderError`
+ when the order can not be reversed using current trivial algorithm.
+ Also raises the same error when `#reverse_order` is called on
+ relation without any order and table has no primary key:
+
+ Topic.order("concat(author_name, title)").reverse_order
+ # Before: SELECT `topics`.* FROM `topics` ORDER BY concat(author_name DESC, title) DESC
+ # After: raises ActiveRecord::IrreversibleOrderError
+ Edge.all.reverse_order
+ # Before: SELECT `edges`.* FROM `edges` ORDER BY `edges`.`` DESC
+ # After: raises ActiveRecord::IrreversibleOrderError
+
+ *Bogdan Gusiev*
+
+* Improve schema_migrations insertion performance by inserting all versions
+ in one INSERT SQL.
+
+ *Akira Matsuda*, *Naoto Koshikawa*
+
+* Using `references` or `belongs_to` in migrations will always add index
+ for the referenced column by default, without adding `index: true` option
+ to generated migration file. Users can opt out of this by passing
+ `index: false`.
+
+ Fixes #18146.
+
+ *Matthew Draper*, *Prathamesh Sonpatki*
+
* Run `type` attributes through attributes API type-casting before
instantiating the corresponding subclass. This makes it possible to define
custom STI mappings.
@@ -968,13 +1004,6 @@
*Alex Coomans*
-* Dump indexes in `create_table` instead of `add_index`.
-
- If the adapter supports indexes in `create_table`, generated SQL is
- slightly more efficient.
-
- *Ryuta Kamizono*
-
* Correctly dump `:options` on `create_table` for MySQL.
*Ryuta Kamizono*
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 0564dca94a..46df733cfe 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -20,6 +20,9 @@ end
desc 'Run mysql2, sqlite, and postgresql tests by default'
task :default => :test
+task :package
+task "package:clean"
+
desc 'Run mysql2, sqlite, and postgresql tests'
task :test do
tasks = defined?(JRUBY_VERSION) ?
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index be88c7c9e8..3ff41ed81b 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -33,7 +33,7 @@ module ActiveRecord
# the database).
#
# class Customer < ActiveRecord::Base
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
+ # composed_of :balance, class_name: "Money", mapping: %w(amount currency)
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
# end
#
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index f6d8e8a342..e13fe33b85 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1593,6 +1593,8 @@ module ActiveRecord
# If true, the associated object will be touched (the updated_at/on attributes set to current time)
# when this record is either saved or destroyed. If you specify a symbol, that attribute
# will be updated with the current time in addition to the updated_at/on attribute.
+ # Please note that with touching no validation is performed and only the +after_touch+,
+ # +after_commit+ and +after_rollback+ callbacks are executed.
# [:inverse_of]
# Specifies the name of the #has_one or #has_many association on the associated
# object that is the inverse of this #belongs_to association. Does not work in
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index f02d146e89..346329c610 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
def self.valid_options(options)
- super + [:foreign_type, :polymorphic, :touch, :counter_cache, :optional]
+ super + [:polymorphic, :touch, :counter_cache, :optional]
end
def self.valid_dependent_options
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index 9d64ae877b..4de846d12b 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
def self.valid_options(options)
- valid = super + [:as, :foreign_type]
+ valid = super + [:as]
valid += [:through, :source, :source_type] if options[:through]
valid
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 58a9c8ff24..bb96202a22 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -3,7 +3,7 @@
module ActiveRecord::Associations::Builder # :nodoc:
class SingularAssociation < Association #:nodoc:
def self.valid_options(options)
- super + [:dependent, :primary_key, :inverse_of, :required]
+ super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
end
def self.define_accessors(model, reflection)
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 473b80a658..9f2c7292ea 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -72,7 +72,10 @@ module ActiveRecord
pk_type = reflection.primary_key_type
ids = Array(ids).reject(&:blank?)
ids.map! { |i| pk_type.cast(i) }
- replace(klass.find(ids).index_by(&:id).values_at(*ids))
+ records = klass.where(reflection.association_primary_key => ids).index_by do |r|
+ r.send(reflection.association_primary_key)
+ end.values_at(*ids)
+ replace(records)
end
def reset
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index deb0f8c9f5..36fc381343 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -66,6 +66,11 @@ module ActiveRecord
through_record = through_association.build(*options_for_through_record)
through_record.send("#{source_reflection.name}=", record)
+
+ if options[:source_type]
+ through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
+ end
+
through_record
end
end
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 be65cf318c..708b3af5bd 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -75,7 +75,7 @@ module ActiveRecord
column = klass.columns_hash[reflection.type.to_s]
binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
- constraint = constraint.and table[reflection.type].eq(Arel::Nodes::BindParam.new)
+ constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
end
joins << table.create_join(table, table.create_on(constraint), join_type)
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index e11a5cfb8a..3032bc786e 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -47,7 +47,7 @@ module ActiveRecord
# This is overridden by HABTM as the condition should be on the foreign_key column in
# the join table
def association_key
- table[association_key_name]
+ klass.arel_attribute(association_key_name, table)
end
# The name of the key on the model which declares the association
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 061628725d..ebaaa54b2b 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -20,7 +20,7 @@ module ActiveRecord
nil
end
else
- map(super) { |t| cast(t) }
+ map_avoiding_infinite_recursion(super) { |v| cast(v) }
end
end
@@ -34,13 +34,23 @@ module ActiveRecord
elsif value.is_a?(::Float)
value
else
- map(value) { |v| convert_time_to_time_zone(v) }
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
end
end
def set_time_zone_without_conversion(value)
::Time.zone.local_to_utc(value).in_time_zone
end
+
+ def map_avoiding_infinite_recursion(value)
+ map(value) do |v|
+ if value.equal?(v)
+ nil
+ else
+ yield(v)
+ end
+ end
+ end
end
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 4a31a1aa84..7ed2fe48be 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -13,7 +13,6 @@ require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/module/introspection'
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/class/subclasses'
-require 'arel'
require 'active_record/attribute_decorators'
require 'active_record/errors'
require 'active_record/log_subscriber'
@@ -132,9 +131,6 @@ module ActiveRecord #:nodoc:
# end
# end
#
- # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
- # or <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
- #
# == Attribute query methods
#
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb
index b0e555038e..5dcc98424a 100644
--- a/activerecord/lib/active_record/collection_cache_key.rb
+++ b/activerecord/lib/active_record/collection_cache_key.rb
@@ -15,12 +15,19 @@ module ActiveRecord
column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
query = collection
+ .unscope(:select)
.select("COUNT(*) AS size", "MAX(#{column}) AS timestamp")
.unscope(:order)
result = connection.select_one(query)
- size = result["size"]
- timestamp = column_type.deserialize(result["timestamp"])
+ if result.blank?
+ size = 0
+ timestamp = nil
+ else
+ size = result["size"]
+ timestamp = column_type.deserialize(result["timestamp"])
+ end
+
end
if timestamp
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 7a2a1a0e33..7e0c9f7837 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -69,7 +69,11 @@ module ActiveRecord
end
undef_method :select_rows
- # Executes the SQL statement in the context of this connection.
+ # Executes the SQL statement in the context of this connection and returns
+ # the raw result from the connection adapter.
+ # Note: depending on your database connector, the result returned by this
+ # method may be manually memory managed. Consider using the exec_query
+ # wrapper instead.
def execute(sql, name = nil)
end
undef_method :execute
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 1cda23dc1d..690e0ba957 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -69,7 +69,7 @@ module ActiveRecord
def initialize(
name,
polymorphic: false,
- index: false,
+ index: true,
foreign_key: false,
type: :integer,
**options
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 70868ebd03..983c4340c6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -92,7 +92,9 @@ module ActiveRecord
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
- def columns(table_name) end
+ def columns(table_name)
+ raise NotImplementedError, "#columns is not implemented"
+ end
# Checks to see if a column exists in a given table.
#
@@ -110,18 +112,25 @@ module ActiveRecord
#
def column_exists?(table_name, column_name, type = nil, options = {})
column_name = column_name.to_s
- columns(table_name).any?{ |c| c.name == column_name &&
- (!type || c.type == type) &&
- (!options.key?(:limit) || c.limit == options[:limit]) &&
- (!options.key?(:precision) || c.precision == options[:precision]) &&
- (!options.key?(:scale) || c.scale == options[:scale]) &&
- (!options.key?(:default) || c.default == options[:default]) &&
- (!options.key?(:null) || c.null == options[:null]) }
+ checks = []
+ checks << lambda { |c| c.name == column_name }
+ checks << lambda { |c| c.type == type } if type
+ (migration_keys - [:name]).each do |attr|
+ checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
+ end
+
+ columns(table_name).any? { |c| checks.all? { |check| check[c] } }
end
# Returns just a table's primary key
def primary_key(table_name)
pks = primary_keys(table_name)
+ warn <<-WARNING.strip_heredoc if pks.count > 1
+ WARNING: Rails does not support composite primary key.
+
+ #{table_name} has composite primary key. Composite primary key is ignored.
+ WARNING
+
pks.first if pks.one?
end
@@ -487,8 +496,6 @@ module ActiveRecord
# Default is (10,0).
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
# <tt>:scale</tt> [0..infinity]. No default.
- # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
- # Internal storage as strings. No default.
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
# but the maximum supported <tt>:precision</tt> is 16. No default.
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
@@ -691,7 +698,7 @@ module ActiveRecord
#
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
#
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
+ # Note: only supported by MySQL.
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -957,9 +964,9 @@ module ActiveRecord
def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::SchemaMigration.order('version').map { |sm|
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
- }.join "\n\n"
+ sql = "INSERT INTO #{sm_table} (version) VALUES "
+ sql << ActiveRecord::SchemaMigration.order('version').pluck(:version).map {|v| "('#{v}')" }.join(', ')
+ sql << ";\n\n"
end
# Should not be called normally, but this operation is non-destructive.
@@ -972,6 +979,10 @@ module ActiveRecord
ActiveRecord::InternalMetadata.create_table
end
+ def internal_string_options_for_primary_key # :nodoc:
+ { primary_key: true }
+ end
+
def assume_migrated_upto_version(version, migrations_paths)
migrations_paths = Array(migrations_paths)
version = version.to_i
@@ -987,14 +998,12 @@ module ActiveRecord
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
end
- inserted = Set.new
- (versions - migrated).each do |v|
- if inserted.include?(v)
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
- elsif v < version
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
- inserted << v
+ inserting = (versions - migrated).select {|v| v < version}
+ if inserting.any?
+ if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
end
+ execute "INSERT INTO #{sm_table} (version) VALUES #{inserting.map {|v| "('#{v}')"}.join(', ') }"
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 14d04a6388..6ecdab6eb0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -167,8 +167,13 @@ module ActiveRecord
def commit_transaction
transaction = @stack.last
- transaction.before_commit_records
- @stack.pop
+
+ begin
+ transaction.before_commit_records
+ ensure
+ @stack.pop
+ end
+
transaction.commit
transaction.commit_records
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 3e84786be0..8751b6da4b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -1,5 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/mysql/column'
+require 'active_record/connection_adapters/mysql/explain_pretty_printer'
require 'active_record/connection_adapters/mysql/schema_creation'
require 'active_record/connection_adapters/mysql/schema_definitions'
require 'active_record/connection_adapters/mysql/schema_dumper'
@@ -31,12 +32,6 @@ module ActiveRecord
class_attribute :emulate_booleans
self.emulate_booleans = true
- LOST_CONNECTION_ERROR_MESSAGES = [
- "Server shutdown in progress",
- "Broken pipe",
- "Lost connection to MySQL server during query",
- "MySQL server has gone away" ]
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
NATIVE_DATABASE_TYPES = {
@@ -57,7 +52,6 @@ module ActiveRecord
INDEX_TYPES = [:fulltext, :spatial]
INDEX_USINGS = [:btree, :hash]
- # FIXME: Make the first parameter more similar for the two adapters
def initialize(connection, logger, connection_options, config)
super(connection, logger, config)
@quoted_column_names, @quoted_table_names = {}, {}
@@ -70,16 +64,18 @@ module ActiveRecord
else
@prepared_statements = false
end
+
+ if version < '5.0.0'
+ raise "Your version of MySQL (#{full_version.match(/^\d+\.\d+\.\d+/)[0]}) is too old. Active Record supports MySQL >= 5.0."
+ end
end
- MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN = 191
CHARSETS_OF_4BYTES_MAXLEN = ['utf8mb4', 'utf16', 'utf16le', 'utf32']
- def initialize_schema_migrations_table
- if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
- ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN)
- else
- ActiveRecord::SchemaMigration.create_table
- end
+
+ def internal_string_options_for_primary_key # :nodoc:
+ super.tap { |options|
+ options[:collation] = collation.sub(/\A[^_]+/, 'utf8') if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
+ }
end
def version
@@ -105,12 +101,8 @@ module ActiveRecord
true
end
- # MySQL 4 technically support transaction isolation, but it is affected by a bug
- # where the transaction level gets persisted for the whole session:
- #
- # http://bugs.mysql.com/bug.php?id=39170
def supports_transaction_isolation?
- version >= '5.0.0'
+ true
end
def supports_explain?
@@ -126,17 +118,15 @@ module ActiveRecord
end
def supports_views?
- version >= '5.0.0'
+ true
end
def supports_datetime_with_precision?
version >= '5.6.4'
end
- # 5.0.0 definitely supports it, possibly supported by earlier versions but
- # not sure
def supports_advisory_locks?
- version >= '5.0.0'
+ true
end
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
@@ -238,72 +228,7 @@ module ActiveRecord
result = exec_query(sql, 'EXPLAIN', binds)
elapsed = Time.now - start
- ExplainPrettyPrinter.new.pp(result, elapsed)
- end
-
- class ExplainPrettyPrinter # :nodoc:
- # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
- # MySQL shell:
- #
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
- # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
- # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
- # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
- # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
- # 2 rows in set (0.00 sec)
- #
- # This is an exercise in Ruby hyperrealism :).
- def pp(result, elapsed)
- widths = compute_column_widths(result)
- separator = build_separator(widths)
-
- pp = []
-
- pp << separator
- pp << build_cells(result.columns, widths)
- pp << separator
-
- result.rows.each do |row|
- pp << build_cells(row, widths)
- end
-
- pp << separator
- pp << build_footer(result.rows.length, elapsed)
-
- pp.join("\n") + "\n"
- end
-
- private
-
- def compute_column_widths(result)
- [].tap do |widths|
- result.columns.each_with_index do |column, i|
- cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
- widths << cells_in_column.map(&:length).max
- end
- end
- end
-
- def build_separator(widths)
- padding = 1
- '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
- end
-
- def build_cells(items, widths)
- cells = []
- items.each_with_index do |item, i|
- item = 'NULL' if item.nil?
- justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
- cells << item.to_s.send(justifier, widths[i])
- end
- '| ' + cells.join(' | ') + ' |'
- end
-
- def build_footer(nrows, elapsed)
- rows_label = nrows == 1 ? 'row' : 'rows'
- "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
- end
+ MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
end
def clear_cache!
@@ -980,7 +905,6 @@ module ActiveRecord
when 3; 'mediumint'
when nil, 4; 'int'
when 5..8; 'bigint'
- when 11; 'int(11)' # backward compatibility with Rails 2.0
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 81de7c03fb..10f908538f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -30,7 +30,7 @@ module ActiveRecord
end
def bigint?
- /bigint/ === sql_type
+ /\Abigint\b/ === sql_type
end
# Returns the human name of the column name.
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index f633892dee..4bc6447368 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -33,7 +33,7 @@ module ActiveRecord
def initialize(url)
raise "Database URL cannot be empty" if url.blank?
@uri = uri_parser.parse(url)
- @adapter = @uri.scheme.tr('-', '_')
+ @adapter = @uri.scheme && @uri.scheme.tr('-', '_')
@adapter = "postgresql" if @adapter == "postgres"
if @uri.opaque
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
new file mode 100644
index 0000000000..1820853196
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb
@@ -0,0 +1,70 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module MySQL
+ class ExplainPrettyPrinter # :nodoc:
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
+ # MySQL shell:
+ #
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+ # | 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
+ # +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
+ # 2 rows in set (0.00 sec)
+ #
+ # This is an exercise in Ruby hyperrealism :).
+ def pp(result, elapsed)
+ widths = compute_column_widths(result)
+ separator = build_separator(widths)
+
+ pp = []
+
+ pp << separator
+ pp << build_cells(result.columns, widths)
+ pp << separator
+
+ result.rows.each do |row|
+ pp << build_cells(row, widths)
+ end
+
+ pp << separator
+ pp << build_footer(result.rows.length, elapsed)
+
+ pp.join("\n") + "\n"
+ end
+
+ private
+
+ def compute_column_widths(result)
+ [].tap do |widths|
+ result.columns.each_with_index do |column, i|
+ cells_in_column = [column] + result.rows.map {|r| r[i].nil? ? 'NULL' : r[i].to_s}
+ widths << cells_in_column.map(&:length).max
+ end
+ end
+ end
+
+ def build_separator(widths)
+ padding = 1
+ '+' + widths.map {|w| '-' * (w + (padding*2))}.join('+') + '+'
+ end
+
+ def build_cells(items, widths)
+ cells = []
+ items.each_with_index do |item, i|
+ item = 'NULL' if item.nil?
+ justifier = item.is_a?(Numeric) ? 'rjust' : 'ljust'
+ cells << item.to_s.send(justifier, widths[i])
+ end
+ '| ' + cells.join(' | ') + ' |'
+ end
+
+ def build_footer(nrows, elapsed)
+ rows_label = nrows == 1 ? 'row' : 'rows'
+ "#{nrows} #{rows_label} in set (%.2f sec)" % elapsed
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index c3c5b660fd..23b33a3555 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -42,7 +42,7 @@ module ActiveRecord
end
def supports_json?
- version >= '5.7.8'
+ !mariadb? && version >= '5.7.8'
end
# HELPER METHODS ===========================================
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 6c15facf3b..6aa264d766 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -4,44 +4,7 @@ module ActiveRecord
module DatabaseStatements
def explain(arel, binds = [])
sql = "EXPLAIN #{to_sql(arel, binds)}"
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
- end
-
- class ExplainPrettyPrinter # :nodoc:
- # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
- # PostgreSQL shell:
- #
- # QUERY PLAN
- # ------------------------------------------------------------------------------
- # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
- # Join Filter: (posts.user_id = users.id)
- # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
- # Index Cond: (id = 1)
- # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
- # Filter: (posts.user_id = 1)
- # (6 rows)
- #
- def pp(result)
- header = result.columns.first
- lines = result.rows.map(&:first)
-
- # We add 2 because there's one char of padding at both sides, note
- # the extra hyphens in the example above.
- width = [header, *lines].map(&:length).max + 2
-
- pp = []
-
- pp << header.center(width).rstrip
- pp << '-' * width
-
- pp += lines.map {|line| " #{line}"}
-
- nrows = result.rows.length
- rows_label = nrows == 1 ? 'row' : 'rows'
- pp << "(#{nrows} #{rows_label})"
-
- pp.join("\n") + "\n"
- end
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
end
def select_value(arel, name = nil, binds = [])
@@ -128,6 +91,8 @@ module ActiveRecord
# Executes an SQL statement, returning a PGresult object on success
# or raising a PGError exception otherwise.
+ # Note: the PGresult object is manually memory managed; if you don't
+ # need it specifically, you many want consider the exec_query wrapper.
def execute(sql, name = nil)
log(sql, name) do
@connection.async_exec(sql)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb
new file mode 100644
index 0000000000..789b88912c
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb
@@ -0,0 +1,42 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ class ExplainPrettyPrinter # :nodoc:
+ # Pretty prints the result of an EXPLAIN in a way that resembles the output of the
+ # PostgreSQL shell:
+ #
+ # QUERY PLAN
+ # ------------------------------------------------------------------------------
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
+ # Join Filter: (posts.user_id = users.id)
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
+ # Index Cond: (id = 1)
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
+ # Filter: (posts.user_id = 1)
+ # (6 rows)
+ #
+ def pp(result)
+ header = result.columns.first
+ lines = result.rows.map(&:first)
+
+ # We add 2 because there's one char of padding at both sides, note
+ # the extra hyphens in the example above.
+ width = [header, *lines].map(&:length).max + 2
+
+ pp = []
+
+ pp << header.center(width).rstrip
+ pp << '-' * width
+
+ pp += lines.map {|line| " #{line}"}
+
+ nrows = result.rows.length
+ rows_label = nrows == 1 ? 'row' : 'rows'
+ pp << "(#{nrows} #{rows_label})"
+
+ pp.join("\n") + "\n"
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index 2163674019..dcc12ae2a4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -3,8 +3,6 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class Money < Type::Decimal # :nodoc:
- class_attribute :precision
-
def type
:money
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2de6fbfaf0..beaeef3c78 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -5,6 +5,7 @@ require 'pg'
require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/postgresql/column"
require "active_record/connection_adapters/postgresql/database_statements"
+require "active_record/connection_adapters/postgresql/explain_pretty_printer"
require "active_record/connection_adapters/postgresql/oid"
require "active_record/connection_adapters/postgresql/quoting"
require "active_record/connection_adapters/postgresql/referential_integrity"
@@ -213,8 +214,8 @@ module ActiveRecord
@statements = StatementPool.new @connection,
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
- if postgresql_version < 80200
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
+ if postgresql_version < 90100
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
end
add_pg_decoders
@@ -296,9 +297,8 @@ module ActiveRecord
true
end
- # Returns true if pg > 9.1
def supports_extensions?
- postgresql_version >= 90100
+ true
end
# Range datatypes weren't introduced until PostgreSQL 9.2
@@ -645,12 +645,6 @@ module ActiveRecord
# connected server's characteristics.
def connect
@connection = PGconn.connect(@connection_parameters)
-
- # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
- # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
- # should know about this but can't detect it there, so deal with it here.
- OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
-
configure_connection
rescue ::PG::Error => error
if error.message.include?("does not exist")
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb
new file mode 100644
index 0000000000..a946f5ebd0
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb
@@ -0,0 +1,19 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ class ExplainPrettyPrinter # :nodoc:
+ # Pretty prints the result of an EXPLAIN QUERY PLAN in a way that resembles
+ # the output of the SQLite shell:
+ #
+ # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
+ # 0|1|1|SCAN TABLE posts (~100000 rows)
+ #
+ def pp(result)
+ result.rows.map do |row|
+ row.join('|')
+ end.join("\n") + "\n"
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index d1893f35f5..c65d33ccb3 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -1,5 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
+require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
require 'active_record/connection_adapters/sqlite3/schema_creation'
gem 'sqlite3', '~> 1.3.6'
@@ -7,7 +8,6 @@ require 'sqlite3'
module ActiveRecord
module ConnectionHandling # :nodoc:
- # sqlite3 adapter reuses sqlite_connection.
def sqlite3_connection(config)
# Require database.
unless config[:database]
@@ -218,21 +218,7 @@ module ActiveRecord
def explain(arel, binds = [])
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
- ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
- end
-
- class ExplainPrettyPrinter
- # Pretty prints the result of an EXPLAIN QUERY PLAN in a way that resembles
- # the output of the SQLite shell:
- #
- # 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
- # 0|1|1|SCAN TABLE posts (~100000 rows)
- #
- def pp(result) # :nodoc:
- result.rows.map do |row|
- row.join('|')
- end.join("\n") + "\n"
- end
+ SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
end
def exec_query(sql, name = nil, binds = [], prepare: false)
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 475a298467..24fd0aaecf 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -256,6 +256,11 @@ module ActiveRecord
end
end
+ def arel_attribute(name, table = arel_table) # :nodoc:
+ name = attribute_alias(name) if attribute_alias?(name)
+ table[name]
+ end
+
def predicate_builder # :nodoc:
@predicate_builder ||= PredicateBuilder.new(table_metadata)
end
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 8655f68308..903c63a7db 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -95,7 +95,7 @@ module ActiveRecord
module Enum
def self.extended(base) # :nodoc:
- base.class_attribute(:defined_enums)
+ base.class_attribute(:defined_enums, instance_writer: false)
base.defined_enums = {}
end
@@ -105,9 +105,10 @@ module ActiveRecord
end
class EnumType < Type::Value # :nodoc:
- def initialize(name, mapping)
+ def initialize(name, mapping, subtype)
@name = name
@mapping = mapping
+ @subtype = subtype
end
def cast(value)
@@ -124,7 +125,7 @@ module ActiveRecord
def deserialize(value)
return if value.nil?
- mapping.key(value)
+ mapping.key(subtype.deserialize(value))
end
def serialize(value)
@@ -139,7 +140,7 @@ module ActiveRecord
protected
- attr_reader :name, :mapping
+ attr_reader :name, :mapping, :subtype
end
def enum(definitions)
@@ -158,7 +159,9 @@ module ActiveRecord
detect_enum_conflict!(name, name)
detect_enum_conflict!(name, "#{name}=")
- attribute name, EnumType.new(name, enum_values)
+ decorate_attribute_type(name, :enum) do |subtype|
+ EnumType.new(name, enum_values, subtype)
+ end
_enum_methods_module.module_eval do
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index e5906b6756..87f32c042c 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -275,4 +275,9 @@ module ActiveRecord
# The mysql2 and postgresql adapters support setting the transaction isolation level.
class TransactionIsolationError < ActiveRecordError
end
+
+ # IrreversibleOrderError is raised when a relation's order is too complex for
+ # +reverse_order+ to automatically reverse.
+ class IrreversibleOrderError < ActiveRecordError
+ end
end
diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb
index ecf4046bff..aa1f5c4fb4 100644
--- a/activerecord/lib/active_record/gem_version.rb
+++ b/activerecord/lib/active_record/gem_version.rb
@@ -8,7 +8,7 @@ module ActiveRecord
MAJOR = 5
MINOR = 0
TINY = 0
- PRE = "beta1"
+ PRE = "beta2"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 3a17f74b1d..899683ee4f 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -52,7 +52,11 @@ module ActiveRecord
attrs = args.first
if has_attribute?(inheritance_column)
- subclass = subclass_from_attributes(attrs) || subclass_from_attributes(column_defaults)
+ subclass = subclass_from_attributes(attrs)
+
+ if subclass.nil? && base_class == self
+ subclass = subclass_from_attributes(column_defaults)
+ end
end
if subclass && subclass != self
@@ -188,7 +192,7 @@ module ActiveRecord
end
def type_condition(table = arel_table)
- sti_column = table[inheritance_column]
+ sti_column = arel_attribute(inheritance_column, table)
sti_names = ([self] + descendants).map(&:sti_name)
sti_column.in(sti_names)
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index e5c6e5c885..81db96bffd 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -14,8 +14,8 @@ module ActiveRecord
"#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
end
- def index_name
- "#{table_name_prefix}unique_#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
+ def original_table_name
+ "#{table_name_prefix}active_record_internal_metadatas#{table_name_suffix}"
end
def []=(key, value)
@@ -30,14 +30,24 @@ module ActiveRecord
ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
end
+ def original_table_exists?
+ # This method will be removed in Rails 5.1
+ # Since it is only necessary when `active_record_internal_metadatas` could exist
+ ActiveSupport::Deprecation.silence { connection.table_exists?(original_table_name) }
+ end
+
# Creates an internal metadata table with columns +key+ and +value+
def create_table
+ if original_table_exists?
+ connection.rename_table(original_table_name, table_name)
+ end
unless table_exists?
+ key_options = connection.internal_string_options_for_primary_key
+
connection.create_table(table_name, id: false) do |t|
- t.column :key, :string
- t.column :value, :string
+ t.string :key, key_options
+ t.string :value
t.timestamps
- t.index :key, unique: true, name: index_name
end
end
end
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index 1b94573870..45e35a4f71 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -5,6 +5,12 @@ module ActiveRecord
module FourTwoShared
module TableDefinition
+ def references(*, **options)
+ options[:index] ||= false
+ super
+ end
+ alias :belongs_to :references
+
def timestamps(*, **options)
options[:null] = true if options[:null].nil?
super
@@ -24,6 +30,25 @@ module ActiveRecord
end
end
+ def change_table(table_name, options = {})
+ if block_given?
+ super(table_name, options) do |t|
+ class << t
+ prepend TableDefinition
+ end
+ yield t
+ end
+ else
+ super
+ end
+ end
+
+ def add_reference(*, **options)
+ options[:index] ||= false
+ super
+ end
+ alias :add_belongs_to :add_reference
+
def add_timestamps(*, **options)
options[:null] = true if options[:null].nil?
super
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 722d7b5fce..ee52c3ae02 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -44,9 +44,9 @@ module ActiveRecord
##
# :singleton-method:
- # Accessor for the name of the internal metadata table. By default, the value is "active_record_internal_metadatas"
+ # Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
class_attribute :internal_metadata_table_name, instance_accessor: false
- self.internal_metadata_table_name = "active_record_internal_metadatas"
+ self.internal_metadata_table_name = "ar_internal_metadata"
##
# :singleton-method:
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index c5a1488588..0d5a8e6f25 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -542,7 +542,7 @@ module ActiveRecord
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
# association and evaluates to +true+.
def reject_new_record?(association_name, attributes)
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
end
# Determines if a record with the particular +attributes+ should be
@@ -551,7 +551,8 @@ module ActiveRecord
#
# Returns false if there is a +destroy_flag+ on the attributes.
def call_reject_if(association_name, attributes)
- return false if has_destroy_flag?(attributes)
+ return false if will_be_destroyed?(association_name, attributes)
+
case callback = self.nested_attributes_options[association_name][:reject_if]
when Symbol
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -560,6 +561,15 @@ module ActiveRecord
end
end
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
+ def will_be_destroyed?(association_name, attributes)
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
+ end
+
+ def allow_destroy?(association_name)
+ self.nested_attributes_options[association_name][:allow_destroy]
+ end
+
def raise_nested_attributes_record_not_found!(association_name, record_id)
model = self.class._reflect_on_association(association_name).klass.name
raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 522c35252f..d9a394fb71 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -102,11 +102,11 @@ module ActiveRecord
# Saves the model.
#
- # If the model is new a record gets created in the database, otherwise
+ # If the model is new, a record gets created in the database, otherwise
# the existing record gets updated.
#
- # By default, save always run validations. If any of them fail the action
- # is cancelled and #save returns +false+. However, if you supply
+ # By default, save always runs validations. If any of them fail the action
+ # is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
# validate: false, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
@@ -132,9 +132,10 @@ module ActiveRecord
# If the model is new, a record gets created in the database, otherwise
# the existing record gets updated.
#
- # With #save! validations always run. If any of them fail
- # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
- # for more information.
+ # By default, #save! always runs validations. If any of them fail
+ # ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
+ # validate: false, validations are bypassed altogether. See
+ # ActiveRecord::Validations for more information.
#
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
# the current time. However, if you supply <tt>touch: false</tt>, these
@@ -246,7 +247,7 @@ module ActiveRecord
# This method raises an ActiveRecord::ActiveRecordError if the
# attribute is marked as readonly.
#
- # See also #update_column.
+ # Also see #update_column.
def update_attribute(name, value)
name = name.to_s
verify_readonly_attribute(name)
@@ -269,7 +270,7 @@ module ActiveRecord
alias update_attributes update
# Updates its receiver just like #update but calls #save! instead
- # of +save+, so an exception is raised if the record is invalid.
+ # of +save+, so an exception is raised if the record is invalid and saving will fail.
def update!(attributes)
# The following transaction covers any possible database side-effects of the
# attributes assignment. For example, setting the IDs of a child collection.
@@ -348,7 +349,7 @@ module ActiveRecord
end
# Wrapper around #decrement that saves the record. This method differs from
- # its non-bang version in that it passes through the attribute setter.
+ # its non-bang version in the sense that it passes through the attribute setter.
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def decrement!(attribute, by = 1)
@@ -373,7 +374,7 @@ module ActiveRecord
end
# Wrapper around #toggle that saves the record. This method differs from
- # its non-bang version in that it passes through the attribute setter.
+ # its non-bang version in the sense that it passes through the attribute setter.
# Saving is not subjected to validation checks. Returns +true+ if the
# record could be saved.
def toggle!(attribute)
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 38916f7376..f4200e96b7 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -40,7 +40,7 @@ module ActiveRecord
task :load_config do
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
- if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
+ if defined?(ENGINE_ROOT) && engine = Rails::Engine.find(ENGINE_ROOT)
if engine.paths['db/migrate'].existent
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index d81d6b54b3..69a7838001 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -302,7 +302,7 @@ db_namespace = namespace :db do
end
desc "Recreates the databases from the structure.sql file"
- task :load => [:load_config] do
+ task :load => [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['SCHEMA'])
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 8b22edcb5b..99c0e71f97 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -7,8 +7,8 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :_reflections
- class_attribute :aggregate_reflections
+ class_attribute :_reflections, instance_writer: false
+ class_attribute :aggregate_reflections, instance_writer: false
self._reflections = {}
self.aggregate_reflections = {}
end
@@ -486,28 +486,7 @@ module ActiveRecord
# Returns +true+ if +self+ is a +has_one+ reflection.
def has_one?; false; end
- def association_class
- case macro
- when :belongs_to
- if polymorphic?
- Associations::BelongsToPolymorphicAssociation
- else
- Associations::BelongsToAssociation
- end
- when :has_many
- if options[:through]
- Associations::HasManyThroughAssociation
- else
- Associations::HasManyAssociation
- end
- when :has_one
- if options[:through]
- Associations::HasOneThroughAssociation
- else
- Associations::HasOneAssociation
- end
- end
- end
+ def association_class; raise NotImplementedError; end
def polymorphic?
options[:polymorphic]
@@ -525,14 +504,7 @@ module ActiveRecord
private
def calculate_constructable(macro, options)
- case macro
- when :belongs_to
- !polymorphic?
- when :has_one
- !options[:through]
- else
- true
- end
+ true
end
# Attempts to find the inverse association name automatically.
@@ -625,34 +597,52 @@ module ActiveRecord
end
class HasManyReflection < AssociationReflection # :nodoc:
- def initialize(name, scope, options, active_record)
- super(name, scope, options, active_record)
- end
-
def macro; :has_many; end
def collection?; true; end
- end
- class HasOneReflection < AssociationReflection # :nodoc:
- def initialize(name, scope, options, active_record)
- super(name, scope, options, active_record)
+ def association_class
+ if options[:through]
+ Associations::HasManyThroughAssociation
+ else
+ Associations::HasManyAssociation
+ end
end
+ end
+ class HasOneReflection < AssociationReflection # :nodoc:
def macro; :has_one; end
def has_one?; true; end
- end
- class BelongsToReflection < AssociationReflection # :nodoc:
- def initialize(name, scope, options, active_record)
- super(name, scope, options, active_record)
+ def association_class
+ if options[:through]
+ Associations::HasOneThroughAssociation
+ else
+ Associations::HasOneAssociation
+ end
end
+ private
+
+ def calculate_constructable(macro, options)
+ !options[:through]
+ end
+ end
+
+ class BelongsToReflection < AssociationReflection # :nodoc:
def macro; :belongs_to; end
def belongs_to?; true; end
+ def association_class
+ if polymorphic?
+ Associations::BelongsToPolymorphicAssociation
+ else
+ Associations::BelongsToAssociation
+ end
+ end
+
def join_keys(association_klass)
key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
JoinKeys.new(key, foreign_key)
@@ -661,6 +651,12 @@ module ActiveRecord
def join_id_for(owner) # :nodoc:
owner[foreign_key]
end
+
+ private
+
+ def calculate_constructable(macro, options)
+ !polymorphic?
+ end
end
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 032b8d4c5d..7e842668c6 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -47,7 +47,7 @@ module ActiveRecord
if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
primary_key_value = connection.next_sequence_value(klass.sequence_name)
- values[klass.arel_table[klass.primary_key]] = primary_key_value
+ values[arel_attribute(klass.primary_key)] = primary_key_value
end
end
@@ -105,6 +105,10 @@ module ActiveRecord
[substitutes, binds]
end
+ def arel_attribute(name) # :nodoc:
+ klass.arel_attribute(name, table)
+ end
+
# Initializes new record from relation while maintaining the current
# scope.
#
@@ -373,9 +377,9 @@ module ActiveRecord
stmt.table(table)
if joins_values.any?
- @klass.connection.join_to_update(stmt, arel, table[primary_key])
+ @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
else
- stmt.key = table[primary_key]
+ stmt.key = arel_attribute(primary_key)
stmt.take(arel.limit)
stmt.order(*arel.orders)
stmt.wheres = arel.constraints
@@ -527,7 +531,7 @@ module ActiveRecord
stmt.from(table)
if joins_values.any?
- @klass.connection.join_to_delete(stmt, arel, table[primary_key])
+ @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
else
stmt.wheres = arel.constraints
end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 54587ae18e..de005e2810 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -204,15 +204,15 @@ module ActiveRecord
yield yielded_relation
break if ids.length < of
- batch_relation = relation.where(table[primary_key].gt(primary_key_offset))
+ batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
end
end
private
def apply_limits(relation, start, finish)
- relation = relation.where(table[primary_key].gteq(start)) if start
- relation = relation.where(table[primary_key].lteq(finish)) if finish
+ relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
+ relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
relation
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index f45844a9ea..54c9af4898 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -155,15 +155,7 @@ module ActiveRecord
# See also #ids.
#
def pluck(*column_names)
- column_names.map! do |column_name|
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
- attribute_alias(column_name)
- else
- column_name.to_s
- end
- end
-
- if loaded? && (column_names - @klass.column_names).empty?
+ if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
return @records.pluck(*column_names)
end
@@ -172,7 +164,7 @@ module ActiveRecord
else
relation = spawn
relation.select_values = column_names.map { |cn|
- columns_hash.key?(cn) ? arel_table[cn] : cn
+ @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
}
result = klass.connection.select_all(relation.arel, nil, bound_attributes)
result.cast_values(klass.attribute_types)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 3cbb12a09d..d48bcea28a 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -147,7 +147,7 @@ module ActiveRecord
def last(limit = nil)
if limit
if order_values.empty? && primary_key
- order(arel_table[primary_key].desc).limit(limit).reverse
+ order(arel_attribute(primary_key).desc).limit(limit).reverse
else
to_a.last(limit)
end
@@ -489,20 +489,19 @@ module ActiveRecord
end
def find_nth(index, offset = nil)
+ # TODO: once the offset argument is removed we rely on offset_index
+ # within find_nth_with_limit, rather than pass it in via
+ # find_nth_with_limit_and_offset
+ if offset
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing an offset argument to find_nth is deprecated,
+ please use Relation#offset instead.
+ MSG
+ end
if loaded?
@records[index]
else
- # TODO: once the offset argument is removed we rely on offset_index
- # within find_nth_with_limit, rather than pass it in via
- # find_nth_with_limit_and_offset
- if offset
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing an offset argument to find_nth is deprecated,
- please use Relation#offset instead.
- MSG
- else
- offset = offset_index
- end
+ offset ||= offset_index
@offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
end
end
@@ -515,7 +514,7 @@ module ActiveRecord
# TODO: once the offset argument is removed from find_nth,
# find_nth_with_limit_and_offset can be merged into this method
relation = if order_values.empty? && primary_key
- order(arel_table[primary_key].asc)
+ order(arel_attribute(primary_key).asc)
else
self
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
index 063150958a..8a910a82fe 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
@@ -3,7 +3,7 @@ module ActiveRecord
class RelationHandler # :nodoc:
def call(attribute, value)
if value.select_values.empty?
- value = value.select(value.klass.arel_table[value.klass.primary_key])
+ value = value.select(value.arel_attribute(value.klass.primary_key))
end
attribute.in(value.arel)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 716b1e8505..91bfa4d131 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1093,8 +1093,8 @@ module ActiveRecord
def arel_columns(columns)
columns.map do |field|
- if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value
- arel_table[field]
+ if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
+ arel_attribute(field)
elsif Symbol === field
connection.quote_table_name(field.to_s)
else
@@ -1104,14 +1104,21 @@ module ActiveRecord
end
def reverse_sql_order(order_query)
- order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
+ if order_query.empty?
+ return [arel_attribute(primary_key).desc] if primary_key
+ raise IrreversibleOrderError,
+ "Relation has no current order and table has no primary key to be used as default order"
+ end
order_query.flat_map do |o|
case o
when Arel::Nodes::Ordering
o.reverse
when String
- o.to_s.split(',').map! do |s|
+ if does_not_support_reverse?(o)
+ raise IrreversibleOrderError, "Order #{o.inspect} can not be reversed automatically"
+ end
+ o.split(',').map! do |s|
s.strip!
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
end
@@ -1121,6 +1128,13 @@ module ActiveRecord
end
end
+ def does_not_support_reverse?(order)
+ #uses sql function with multiple arguments
+ order =~ /\([^()]*,[^()]*\)/ ||
+ # uses "nulls first" like construction
+ order =~ /nulls (first|last)\Z/i
+ end
+
def build_order(arel)
orders = order_values.uniq
orders.reject!(&:blank?)
@@ -1156,12 +1170,10 @@ module ActiveRecord
order_args.map! do |arg|
case arg
when Symbol
- arg = klass.attribute_alias(arg) if klass.attribute_alias?(arg)
- table[arg].asc
+ arel_attribute(arg).asc
when Hash
arg.map { |field, dir|
- field = klass.attribute_alias(field) if klass.attribute_alias?(field)
- table[field].send(dir.downcase)
+ arel_attribute(field).send(dir.downcase)
}
else
arg
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 65005bd44b..f115c7542b 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -178,11 +178,11 @@ HEADER
tbl.puts
end
- indexes(table, tbl)
-
tbl.puts " end"
tbl.puts
+ indexes(table, tbl)
+
tbl.rewind
stream.print tbl.read
rescue => e
@@ -198,7 +198,8 @@ HEADER
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
statement_parts = [
- "t.index #{index.columns.inspect}",
+ "add_index #{remove_prefix_and_suffix(index.table).inspect}",
+ index.columns.inspect,
"name: #{index.name.inspect}",
]
statement_parts << 'unique: true' if index.unique
@@ -212,10 +213,11 @@ HEADER
statement_parts << "using: #{index.using.inspect}" if index.using
statement_parts << "type: #{index.type.inspect}" if index.type
- " #{statement_parts.join(', ')}"
+ " #{statement_parts.join(', ')}"
end
stream.puts add_index_statements.sort.join("\n")
+ stream.puts
end
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index ee4c71f304..b6cb233e03 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -16,31 +16,22 @@ module ActiveRecord
"#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
end
- def index_name
- "#{table_name_prefix}unique_#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
- end
-
def table_exists?
ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
end
- def create_table(limit=nil)
+ def create_table
unless table_exists?
- version_options = {null: false}
- version_options[:limit] = limit if limit
+ version_options = connection.internal_string_options_for_primary_key
connection.create_table(table_name, id: false) do |t|
- t.column :version, :string, version_options
- t.index :version, unique: true, name: index_name
+ t.string :version, version_options
end
end
end
def drop_table
- if table_exists?
- connection.remove_index table_name, name: index_name
- connection.drop_table(table_name)
- end
+ connection.drop_table table_name, if_exists: true
end
def normalize_migration_number(number)
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 8baf3b8044..f6b6768ce3 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Stores the default scope for the class.
class_attribute :default_scopes, instance_writer: false, instance_predicate: false
- class_attribute :default_scope_override, instance_predicate: false
+ class_attribute :default_scope_override, instance_writer: false, instance_predicate: false
self.default_scopes = []
self.default_scope_override = nil
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 103569c84d..5395bd6076 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -151,6 +151,7 @@ module ActiveRecord
"a class method with the same name."
end
+ valid_scope_name?(name)
extension = Module.new(&block) if block
if body.respond_to?(:to_proc)
@@ -169,6 +170,15 @@ module ActiveRecord
end
end
end
+
+ protected
+
+ def valid_scope_name?(name)
+ if respond_to?(name, true)
+ logger.warn "Creating scope :#{name}. " \
+ "Overwriting existing method #{self.name}.#{name}."
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb
index f9bb1cf5e0..0faad48ce3 100644
--- a/activerecord/lib/active_record/table_metadata.rb
+++ b/activerecord/lib/active_record/table_metadata.rb
@@ -22,7 +22,11 @@ module ActiveRecord
end
def arel_attribute(column_name)
- arel_table[column_name]
+ if klass
+ klass.arel_attribute(column_name, arel_table)
+ else
+ arel_table[column_name]
+ end
end
def type(column_name)
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index 107f107dc4..481c70201b 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -19,7 +19,11 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
def change
create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
<%- attributes.each do |attribute| -%>
+ <%- if attribute.reference? -%>
+ t.references :<%= attribute.name %><%= attribute.inject_options %>
+ <%- else -%>
<%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
+ <%- end -%>
<%- end -%>
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index bb89e893e0..35dbc76d1b 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -18,4 +18,9 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
column = EnumTest.columns_hash['enum_column']
assert_not column.unsigned?
end
+
+ def test_should_not_be_bigint
+ column = EnumTest.columns_hash['enum_column']
+ assert_not column.bigint?
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index 396f235e77..7c89fda582 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -12,9 +12,30 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
end
def test_initializes_schema_migrations_for_encoding_utf8mb4
- smtn = ActiveRecord::Migrator.schema_migrations_table_name
- connection.drop_table smtn, if_exists: true
+ with_encoding_utf8mb4 do
+ table_name = ActiveRecord::SchemaMigration.table_name
+ connection.drop_table table_name, if_exists: true
+ connection.initialize_schema_migrations_table
+
+ assert connection.column_exists?(table_name, :version, :string, collation: 'utf8_general_ci')
+ end
+ end
+
+ def test_initializes_internal_metadata_for_encoding_utf8mb4
+ with_encoding_utf8mb4 do
+ table_name = ActiveRecord::InternalMetadata.table_name
+ connection.drop_table table_name, if_exists: true
+
+ connection.initialize_internal_metadata_table
+
+ assert connection.column_exists?(table_name, :key, :string, collation: 'utf8_general_ci')
+ end
+ end
+
+ private
+
+ def with_encoding_utf8mb4
database_name = connection.current_database
database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
@@ -23,15 +44,11 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
- connection.initialize_schema_migrations_table
-
- limit = ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN
- assert connection.column_exists?(smtn, :version, :string, limit: limit)
+ yield
ensure
execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
end
- private
def connection
@connection ||= ActiveRecord::Base.connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index b2a805333c..b56c226763 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -24,9 +24,9 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
return skip("no extension support")
end
- @old_schema_migration_tabel_name = ActiveRecord::SchemaMigration.table_name
- @old_tabel_name_prefix = ActiveRecord::Base.table_name_prefix
- @old_tabel_name_suffix = ActiveRecord::Base.table_name_suffix
+ @old_schema_migration_table_name = ActiveRecord::SchemaMigration.table_name
+ @old_table_name_prefix = ActiveRecord::Base.table_name_prefix
+ @old_table_name_suffix = ActiveRecord::Base.table_name_suffix
ActiveRecord::Base.table_name_prefix = "p_"
ActiveRecord::Base.table_name_suffix = "_s"
@@ -36,11 +36,11 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
end
def teardown
- ActiveRecord::Base.table_name_prefix = @old_tabel_name_prefix
- ActiveRecord::Base.table_name_suffix = @old_tabel_name_suffix
+ ActiveRecord::Base.table_name_prefix = @old_table_name_prefix
+ ActiveRecord::Base.table_name_suffix = @old_table_name_suffix
ActiveRecord::SchemaMigration.delete_all rescue nil
ActiveRecord::Migration.verbose = true
- ActiveRecord::SchemaMigration.table_name = @old_schema_migration_tabel_name
+ ActiveRecord::SchemaMigration.table_name = @old_schema_migration_table_name
super
end
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index f1995b243a..31e87722d9 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -53,12 +53,6 @@ module ActiveRecord
end
end
- def test_composite_primary_key
- with_example_table 'id serial, number serial, PRIMARY KEY (id, number)' do
- assert_nil @connection.primary_key('ex')
- end
- end
-
def test_primary_key_raises_error_if_table_not_found
assert_raises(ActiveRecord::StatementInvalid) do
@connection.primary_key('unobtainium')
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 4aeca4d709..f50fe88b9b 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -166,7 +166,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
end
- def test_raise_wraped_exception_on_bad_prepare
+ def test_raise_wrapped_exception_on_bad_prepare
assert_raises(ActiveRecord::StatementInvalid) do
@connection.exec_query "select * from developers where id = ?", 'sql', [bind_param(1)]
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 038d9e2d0f..02c3358ba6 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -400,12 +400,6 @@ module ActiveRecord
end
end
- def test_composite_primary_key
- with_example_table 'id integer, number integer, foo integer, PRIMARY KEY (id, number)' do
- assert_nil @conn.primary_key('ex')
- end
- end
-
def test_supports_extensions
assert_not @conn.supports_extensions?, 'does not support extensions'
end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 1f32c48b95..9c99689c1e 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -21,7 +21,7 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Migration.verbose = @original_verbose
end
- def test_has_has_primary_key
+ def test_has_primary_key
old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
assert_equal "version", ActiveRecord::SchemaMigration.primary_key
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index a531e0e02c..a4298a25a6 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -177,14 +177,14 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
def test_dont_add_if_before_callback_raises_exception
- assert !@david.unchangable_posts.include?(@authorless)
+ assert !@david.unchangeable_posts.include?(@authorless)
begin
- @david.unchangable_posts << @authorless
+ @david.unchangeable_posts << @authorless
rescue Exception
end
assert @david.post_log.empty?
- assert !@david.unchangable_posts.include?(@authorless)
+ assert !@david.unchangeable_posts.include?(@authorless)
@david.reload
- assert !@david.unchangable_posts.include?(@authorless)
+ assert !@david.unchangeable_posts.include?(@authorless)
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index ccb062f991..5c4586da19 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -827,12 +827,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_no_queries { david.projects.columns }
end
- def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
+ def test_attributes_are_being_set_when_initialized_from_habtm_association_with_where_clause
new_developer = projects(:action_controller).developers.where(:name => "Marcelo").build
assert_equal new_developer.name, "Marcelo"
end
- def test_attributes_are_being_set_when_initialized_from_habm_association_with_multiple_where_clauses
+ def test_attributes_are_being_set_when_initialized_from_habtm_association_with_multiple_where_clauses
new_developer = projects(:action_controller).developers.where(:name => "Marcelo").where(:salary => 90_000).build
assert_equal new_developer.name, "Marcelo"
assert_equal new_developer.salary, 90_000
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ad157582a4..ecaa521283 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -2271,7 +2271,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [], authors(:david).posts_with_signature.map(&:title)
end
- test 'associations autosaves when object is already persited' do
+ test 'associations autosaves when object is already persisted' do
bulb = Bulb.create!
tyre = Tyre.create!
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 226ecf5447..bb8c9fa19c 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -884,7 +884,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
ids = [Developer.first.id, -9999]
- assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids}
+ assert_raises(ActiveRecord::AssociationTypeMismatch) {company.developer_ids= ids}
end
def test_build_a_model_from_hm_through_association_with_where_clause
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 94dfbc3346..1db52af59b 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -714,6 +714,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_time_zone_aware_attributes_dont_recurse_infinitely_on_invalid_values
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new(bonus_time: [])
+ assert_equal nil, record.bonus_time
+ end
+ end
+
def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
@@ -791,7 +798,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nil computer.system
end
- def test_global_methods_are_overwritte_when_subclassing
+ def test_global_methods_are_overwritten_when_subclassing
klass = Class.new(ActiveRecord::Base) { self.abstract_class = true }
subklass = Class.new(klass) do
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index ecdf508e3e..eef2d29d02 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -804,7 +804,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal dev.salary.amount, dup.salary.amount
assert !dup.persisted?
- # test if the attributes have been dupd
+ # test if the attributes have been duped
original_amount = dup.salary.amount
dev.salary.amount = 1
assert_equal original_amount, dup.salary.amount
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index 93e7b9cff6..a2874438c1 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -74,5 +74,16 @@ module ActiveRecord
assert_match(/\Acomments\/query-(\h+)-0\Z/, empty_loaded_collection.cache_key)
end
+
+ test "cache_key for queries with offset which return 0 rows" do
+ developers = Developer.offset(20)
+ assert_match(/\Adevelopers\/query-(\h+)-0\Z/, developers.cache_key)
+ end
+
+ test "cache_key with a relation having selected columns" do
+ developers = Developer.select(:salary)
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)
+ end
end
end
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 3c2f5d4219..358b6ad537 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -57,6 +57,12 @@ module ActiveRecord
"encoding" => "utf8" }, spec)
end
+ def test_url_missing_scheme
+ spec = resolve 'foo'
+ assert_equal({
+ "database" => "foo" }, spec)
+ end
+
def test_url_host_db
spec = resolve 'abstract://foo/bar?encoding=utf8'
assert_equal({
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 69b0487dd8..067513e24c 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -201,8 +201,7 @@ if current_adapter?(:Mysql2Adapter)
assert_equal '0', klass.columns_hash['zero'].default
assert !klass.columns_hash['zero'].null
- # 0 in MySQL 4, nil in 5.
- assert [0, nil].include?(klass.columns_hash['omit'].default)
+ assert_equal nil, klass.columns_hash['omit'].default
assert !klass.columns_hash['omit'].null
assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 7c930de97b..babacd1ee9 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -411,4 +411,14 @@ class EnumTest < ActiveRecord::TestCase
assert book.proposed?, "expected fixture to default to proposed status"
assert book.in_english?, "expected fixture to default to english language"
end
+
+ test "uses default value from database on initialization" do
+ book = Book.new
+ assert book.proposed?
+ end
+
+ test "uses default value from database on initialization when using custom mapping" do
+ book = Book.new
+ assert book.hard?
+ end
end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index c870247a4a..7da6842047 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -493,6 +493,10 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
assert_equal 'Firm', firm.type
assert_instance_of Firm, firm
+ client = Client.new
+ assert_equal 'Client', client.type
+ assert_instance_of Client, client
+
firm = Company.new(type: 'Client') # overwrite the default type
assert_equal 'Client', firm.type
assert_instance_of Client, firm
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index b1e1d72944..6d5b6243db 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -53,6 +53,54 @@ module ActiveRecord
ActiveRecord::Migrator.new(:up, [migration]).migrate
assert_not connection.index_exists?(:testings, :bar)
end
+
+ def test_references_does_not_add_index_by_default
+ migration = Class.new(ActiveRecord::Migration) {
+ def migrate(x)
+ create_table :more_testings do |t|
+ t.references :foo
+ t.belongs_to :bar, index: false
+ end
+ end
+ }.new
+
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ assert_not connection.index_exists?(:more_testings, :foo_id)
+ assert_not connection.index_exists?(:more_testings, :bar_id)
+ ensure
+ connection.drop_table :more_testings rescue nil
+ end
+
+ def test_timestamps_have_null_constraints_if_not_present_in_migration_of_create_table
+ migration = Class.new(ActiveRecord::Migration) {
+ def migrate(x)
+ create_table :more_testings do |t|
+ t.timestamps
+ end
+ end
+ }.new
+
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ assert connection.columns(:more_testings).find { |c| c.name == 'created_at' }.null
+ assert connection.columns(:more_testings).find { |c| c.name == 'updated_at' }.null
+ ensure
+ connection.drop_table :more_testings rescue nil
+ end
+
+ def test_timestamps_have_null_constraints_if_not_present_in_migration_for_adding_timestamps_to_existing_table
+ migration = Class.new(ActiveRecord::Migration) {
+ def migrate(x)
+ add_timestamps :testings
+ end
+ }.new
+
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ assert connection.columns(:testings).find { |c| c.name == 'created_at' }.null
+ assert connection.columns(:testings).find { |c| c.name == 'updated_at' }.null
+ 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 edbc8abe4d..b01415afb2 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -32,10 +32,10 @@ module ActiveRecord
assert_equal [], @connection.foreign_keys("testings")
end
- test "foreign keys can be created in one query" do
+ test "foreign keys can be created in one query when index is not added" do
assert_queries(1) do
@connection.create_table :testings do |t|
- t.references :testing_parent, foreign_key: true
+ t.references :testing_parent, foreign_key: true, index: false
end
end
end
diff --git a/activerecord/test/cases/migration/references_index_test.rb b/activerecord/test/cases/migration/references_index_test.rb
index ad6b828d0b..a9a7f0f4c4 100644
--- a/activerecord/test/cases/migration/references_index_test.rb
+++ b/activerecord/test/cases/migration/references_index_test.rb
@@ -23,12 +23,12 @@ module ActiveRecord
assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
end
- def test_does_not_create_index
+ def test_creates_index_by_default_even_if_index_option_is_not_passed
connection.create_table table_name do |t|
t.references :foo
end
- assert_not connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
end
def test_does_not_create_index_explicit
@@ -68,13 +68,13 @@ module ActiveRecord
assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
end
- def test_does_not_create_index_for_existing_table
+ def test_creates_index_for_existing_table_even_if_index_option_is_not_passed
connection.create_table table_name
connection.change_table table_name do |t|
t.references :foo
end
- assert_not connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
+ assert connection.index_exists?(table_name, :foo_id, :name => :index_testings_on_foo_id)
end
def test_does_not_create_index_for_existing_table_explicit
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index f613fd66c3..b9ce6bbc55 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -30,14 +30,14 @@ module ActiveRecord
assert column_exists?(table_name, :taggable_type, :string)
end
- def test_creates_reference_id_index
- add_reference table_name, :user, index: true
- assert index_exists?(table_name, :user_id)
+ def test_does_not_create_reference_id_index_if_index_is_false
+ add_reference table_name, :user, index: false
+ assert_not index_exists?(table_name, :user_id)
end
- def test_does_not_create_reference_id_index
+ def test_create_reference_id_index_even_if_index_option_is_passed
add_reference table_name, :user
- assert_not index_exists?(table_name, :user_id)
+ assert index_exists?(table_name, :user_id)
end
def test_creates_polymorphic_index
diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb
deleted file mode 100644
index 24cba84a09..0000000000
--- a/activerecord/test/cases/migration/table_and_index_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- class Migration
- class TableAndIndexTest < ActiveRecord::TestCase
- def test_add_schema_info_respects_prefix_and_suffix
- conn = ActiveRecord::Base.connection
-
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name, if_exists: true)
- # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name, if_exists: true)
-
- conn.initialize_schema_migrations_table
-
- assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- end
- end
- end
-end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 99e288c25b..bae0467e72 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -355,14 +355,14 @@ class MigrationTest < ActiveRecord::TestCase
def test_internal_metadata_table_name
original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name
- assert_equal "active_record_internal_metadatas", ActiveRecord::InternalMetadata.table_name
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
+ assert_equal "ar_internal_metadata", ActiveRecord::InternalMetadata.table_name
+ ActiveRecord::Base.table_name_prefix = "p_"
+ ActiveRecord::Base.table_name_suffix = "_s"
Reminder.reset_table_name
- assert_equal "prefix_active_record_internal_metadatas_suffix", ActiveRecord::InternalMetadata.table_name
+ assert_equal "p_ar_internal_metadata_s", ActiveRecord::InternalMetadata.table_name
ActiveRecord::Base.internal_metadata_table_name = "changed"
Reminder.reset_table_name
- assert_equal "prefix_changed_suffix", ActiveRecord::InternalMetadata.table_name
+ assert_equal "p_changed_s", ActiveRecord::InternalMetadata.table_name
ActiveRecord::Base.table_name_prefix = ""
ActiveRecord::Base.table_name_suffix = ""
Reminder.reset_table_name
@@ -424,6 +424,21 @@ class MigrationTest < ActiveRecord::TestCase
ENV["RACK_ENV"] = original_rack_env
end
+ def test_rename_internal_metadata_table
+ original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name
+
+ ActiveRecord::Base.internal_metadata_table_name = "active_record_internal_metadatas"
+ Reminder.reset_table_name
+
+ ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
+ Reminder.reset_table_name
+
+ assert_equal "ar_internal_metadata", ActiveRecord::InternalMetadata.table_name
+ ensure
+ ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
+ Reminder.reset_table_name
+ end
+
def test_proper_table_name_on_migration
reminder_class = new_isolated_reminder_class
migration = ActiveRecord::Migration.new
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 0b700afcb4..6fbc6196cc 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -146,6 +146,19 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
assert man.reload.interests.empty?
end
+ def test_reject_if_is_not_short_circuited_if_allow_destroy_is_false
+ Pirate.accepts_nested_attributes_for :ship, reject_if: ->(a) { a[:name] == "The Golden Hind" }, allow_destroy: false
+
+ pirate = Pirate.create!(catchphrase: "Stop wastin' me time", ship_attributes: { name: "White Pearl", _destroy: "1" })
+ assert_equal "White Pearl", pirate.reload.ship.name
+
+ pirate.update!(ship_attributes: { id: pirate.ship.id, name: "The Golden Hind", _destroy: "1" })
+ assert_equal "White Pearl", pirate.reload.ship.name
+
+ pirate.update!(ship_attributes: { id: pirate.ship.id, name: "Black Pearl", _destroy: "1" })
+ assert_equal "Black Pearl", pirate.reload.ship.name
+ end
+
def test_has_many_association_updating_a_single_record
Man.accepts_nested_attributes_for(:interests)
man = Man.create(name: 'John')
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index af15e63d9c..56092aaa0c 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -183,7 +183,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
end
- def test_dupd_becomes_persists_changes_from_the_original
+ def test_duped_becomes_persists_changes_from_the_original
original = topics(:first)
copy = original.dup.becomes(Reply)
copy.save!
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 7e18313c00..b918b36b94 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -135,22 +135,20 @@ class PrimaryKeysTest < ActiveRecord::TestCase
end
end
- def test_primary_key_returns_value_if_it_exists
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers'
- end
+ if ActiveRecord::Base.connection.supports_primary_key?
+ def test_primary_key_returns_value_if_it_exists
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'developers'
+ end
- if ActiveRecord::Base.connection.supports_primary_key?
assert_equal 'id', klass.primary_key
end
- end
- def test_primary_key_returns_nil_if_it_does_not_exist
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'developers_projects'
- end
+ def test_primary_key_returns_nil_if_it_does_not_exist
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'developers_projects'
+ end
- if ActiveRecord::Base.connection.supports_primary_key?
assert_nil klass.primary_key
end
end
@@ -262,6 +260,13 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
assert_equal ["region", "code"], @connection.primary_keys("barcodes")
end
+ def test_primary_key_issues_warning
+ warning = capture(:stderr) do
+ assert_nil @connection.primary_key("barcodes")
+ end
+ assert_match(/WARNING: Rails does not support composite primary key\./, warning)
+ end
+
def test_collectly_dump_composite_primary_key
schema = dump_table_schema "barcodes"
assert_match %r{create_table "barcodes", primary_key: \["region", "code"\]}, schema
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 9c04a41e69..710c86b151 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -23,6 +23,7 @@ require 'models/chef'
require 'models/department'
require 'models/cake_designer'
require 'models/drink_designer'
+require 'models/mocktail_designer'
require 'models/recipe'
class ReflectionTest < ActiveRecord::TestCase
@@ -278,6 +279,15 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal 2, @hotel.chefs.size
end
+ def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case_and_sti
+ @hotel = Hotel.create!
+ @hotel.mocktail_designers << MocktailDesigner.create!
+
+ assert_equal 1, @hotel.mocktail_designers.size
+ assert_equal 1, @hotel.mocktail_designers.count
+ assert_equal 1, @hotel.chef_lists.size
+ end
+
def test_scope_chain_of_polymorphic_association_does_not_leak_into_other_hmt_associations
hotel = Hotel.create!
department = hotel.departments.create!
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index d0f60a84b5..ffb2da7a26 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -26,6 +26,10 @@ module ActiveRecord
def sanitize_sql_for_order(sql)
sql
end
+
+ def arel_attribute(name, table)
+ table[name]
+ end
end
def relation
diff --git a/activerecord/test/cases/relation/record_fetch_warning_test.rb b/activerecord/test/cases/relation/record_fetch_warning_test.rb
index 62f0a7cc49..53daf436e5 100644
--- a/activerecord/test/cases/relation/record_fetch_warning_test.rb
+++ b/activerecord/test/cases/relation/record_fetch_warning_test.rb
@@ -7,7 +7,7 @@ module ActiveRecord
def test_warn_on_records_fetched_greater_than
original_logger = ActiveRecord::Base.logger
- orginal_warn_on_records_fetched_greater_than = ActiveRecord::Base.warn_on_records_fetched_greater_than
+ original_warn_on_records_fetched_greater_than = ActiveRecord::Base.warn_on_records_fetched_greater_than
log = StringIO.new
ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
@@ -22,7 +22,7 @@ module ActiveRecord
assert_match(/Query fetched/, log.string)
ensure
ActiveRecord::Base.logger = original_logger
- ActiveRecord::Base.warn_on_records_fetched_greater_than = orginal_warn_on_records_fetched_greater_than
+ ActiveRecord::Base.warn_on_records_fetched_greater_than = original_warn_on_records_fetched_greater_than
end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0638edacbd..090b885dd5 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -19,6 +19,7 @@ require 'models/aircraft'
require "models/possession"
require "models/reader"
require "models/categorization"
+require "models/edge"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
@@ -223,6 +224,39 @@ class RelationTest < ActiveRecord::TestCase
assert_equal topics(:fifth).title, topics.first.title
end
+ def test_reverse_order_with_function
+ topics = Topic.order("length(title)").reverse_order
+ assert_equal topics(:second).title, topics.first.title
+ end
+
+ def test_reverse_order_with_function_other_predicates
+ topics = Topic.order("author_name, length(title), id").reverse_order
+ assert_equal topics(:second).title, topics.first.title
+ topics = Topic.order("length(author_name), id, length(title)").reverse_order
+ assert_equal topics(:fifth).title, topics.first.title
+ end
+
+ def test_reverse_order_with_multiargument_function
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("concat(author_name, title)").reverse_order
+ end
+ end
+
+ def test_reverse_order_with_nulls_first_or_last
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("title NULLS FIRST").reverse_order
+ end
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Topic.order("title nulls last").reverse_order
+ end
+ end
+
+ def test_default_reverse_order_on_table_without_primary_key
+ assert_raises(ActiveRecord::IrreversibleOrderError) do
+ Edge.all.reverse_order
+ end
+ end
+
def test_order_with_hash_and_symbol_generates_the_same_sql
assert_equal Topic.order(:id).to_sql, Topic.order(:id => :asc).to_sql
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 7b93d20e05..8def74e75b 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -38,7 +38,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{create_table "accounts"}, output
assert_match %r{create_table "authors"}, output
assert_no_match %r{create_table "schema_migrations"}, output
- assert_no_match %r{create_table "active_record_internal_metadatas"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
end
def test_schema_dump_uses_force_cascade_on_create_table
@@ -159,7 +159,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_no_match %r{create_table "accounts"}, output
assert_match %r{create_table "authors"}, output
assert_no_match %r{create_table "schema_migrations"}, output
- assert_no_match %r{create_table "active_record_internal_metadatas"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
end
def test_schema_dump_with_regexp_ignored_table
@@ -167,28 +167,28 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_no_match %r{create_table "accounts"}, output
assert_match %r{create_table "authors"}, output
assert_no_match %r{create_table "schema_migrations"}, output
- assert_no_match %r{create_table "active_record_internal_metadatas"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
end
def test_schema_dumps_index_columns_in_right_order
- index_definition = standard_dump.split(/\n/).grep(/t\.index.*company_index/).first.strip
+ index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
- assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", using: :btree', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type", "rating"], name: "company_index", using: :btree', index_definition
else
- assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type", "rating"], name: "company_index"', index_definition
end
end
def test_schema_dumps_partial_indices
- index_definition = standard_dump.split(/\n/).grep(/t\.index.*company_partial_index/).first.strip
+ index_definition = standard_dump.split(/\n/).grep(/add_index.*company_partial_index/).first.strip
if current_adapter?(:PostgreSQLAdapter)
- assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "(rating > 10)", using: :btree', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "(rating > 10)", using: :btree', index_definition
elsif current_adapter?(:Mysql2Adapter)
- assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", using: :btree', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", using: :btree', index_definition
elsif current_adapter?(:SQLite3Adapter) && ActiveRecord::Base.connection.supports_partial_index?
- assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "rating > 10"', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "rating > 10"', index_definition
else
- assert_equal 't.index ["firm_id", "type"], name: "company_partial_index"', index_definition
+ assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index"', index_definition
end
end
@@ -235,8 +235,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dumps_index_type
output = standard_dump
- assert_match %r{t\.index \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
- assert_match %r{t\.index \["pizza"\], name: "index_key_tests_on_pizza", using: :btree}, output
+ assert_match %r{add_index "key_tests", \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
+ assert_match %r{add_index "key_tests", \["pizza"\], name: "index_key_tests_on_pizza", using: :btree}, output
end
end
@@ -345,7 +345,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_no_match %r{create_table "foo_.+_bar"}, output
assert_no_match %r{add_index "foo_.+_bar"}, output
assert_no_match %r{create_table "schema_migrations"}, output
- assert_no_match %r{create_table "active_record_internal_metadatas"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
if ActiveRecord::Base.connection.supports_foreign_keys?
assert_no_match %r{add_foreign_key "foo_.+_bar"}, output
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 7a8eaeccb7..acba97bbb8 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -440,6 +440,25 @@ class NamedScopingTest < ActiveRecord::TestCase
end
end
+ def test_scopes_with_reserved_names
+ class << Topic
+ def public_method; end
+ public :public_method
+
+ def protected_method; end
+ protected :protected_method
+
+ def private_method; end
+ private :private_method
+ end
+
+ [:public_method, :protected_method, :private_method].each do |reserved_method|
+ assert Topic.respond_to?(reserved_method, true)
+ ActiveRecord::Base.logger.expects(:warn)
+ silence_warnings { Topic.scope(reserved_method, -> { }) }
+ end
+ end
+
def test_scopes_on_relations
# Topic.replied
approved_topics = Topic.all.approved.order('id DESC')
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index e9cdf94c99..ab63f5825c 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -104,7 +104,7 @@ class StoreTest < ActiveRecord::TestCase
assert_equal true, user.settings.instance_of?(ActiveSupport::HashWithIndifferentAccess)
end
- test "convert store attributes from any format other than Hash or HashWithIndifferent access losing the data" do
+ test "convert store attributes from any format other than Hash or HashWithIndifferentAccess losing the data" do
@john.json_data = "somedata"
@john.height = 'low'
assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess)
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 637f89196e..8a7f19293d 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -34,6 +34,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
+ before_commit { |record| record.do_before_commit(nil) }
after_commit { |record| record.do_after_commit(nil) }
after_create_commit { |record| record.do_after_commit(:create) }
after_update_commit { |record| record.do_after_commit(:update) }
@@ -47,6 +48,12 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@history ||= []
end
+ def before_commit_block(on = nil, &block)
+ @before_commit ||= {}
+ @before_commit[on] ||= []
+ @before_commit[on] << block
+ end
+
def after_commit_block(on = nil, &block)
@after_commit ||= {}
@after_commit[on] ||= []
@@ -59,6 +66,11 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@after_rollback[on] << block
end
+ def do_before_commit(on)
+ blocks = @before_commit[on] if defined?(@before_commit)
+ blocks.each{|b| b.call(self)} if blocks
+ end
+
def do_after_commit(on)
blocks = @after_commit[on] if defined?(@after_commit)
blocks.each{|b| b.call(self)} if blocks
@@ -74,6 +86,20 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@first = TopicWithCallbacks.find(1)
end
+ # FIXME: Test behavior, not implementation.
+ def test_before_commit_exception_should_pop_transaction_stack
+ @first.before_commit_block { raise 'better pop this txn from the stack!' }
+
+ original_txn = @first.class.connection.current_transaction
+
+ begin
+ @first.save!
+ fail
+ rescue
+ assert_equal original_txn, @first.class.connection.current_transaction
+ end
+ end
+
def test_call_after_commit_after_transaction_commits
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 0d90cbb110..f25e31b13d 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -75,7 +75,7 @@ class Author < ActiveRecord::Base
has_many :posts_with_multiple_callbacks, :class_name => "Post",
:before_add => [:log_before_adding, Proc.new {|o, r| o.post_log << "before_adding_proc#{r.id || '<new>'}"}],
:after_add => [:log_after_adding, Proc.new {|o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}"}]
- has_many :unchangable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
+ has_many :unchangeable_posts, :class_name => "Post", :before_add => :raise_exception, :after_add => :log_after_adding
has_many :categorizations
has_many :categories, :through => :categorizations
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index 1927191393..e43e5c3901 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -14,6 +14,7 @@ class Book < ActiveRecord::Base
enum author_visibility: [:visible, :invisible], _prefix: true
enum illustrator_visibility: [:visible, :invisible], _prefix: true
enum font_size: [:small, :medium, :large], _prefix: :with, _suffix: true
+ enum cover: { hard: 'hard', soft: 'soft' }
def published!
super
diff --git a/activerecord/test/models/chef.rb b/activerecord/test/models/chef.rb
index 698a52e045..9d3dd01016 100644
--- a/activerecord/test/models/chef.rb
+++ b/activerecord/test/models/chef.rb
@@ -2,3 +2,7 @@ class Chef < ActiveRecord::Base
belongs_to :employable, polymorphic: true
has_many :recipes
end
+
+class ChefList < Chef
+ belongs_to :employable_list, polymorphic: true
+end
diff --git a/activerecord/test/models/hotel.rb b/activerecord/test/models/hotel.rb
index 491f8dfde3..9c90ffcff4 100644
--- a/activerecord/test/models/hotel.rb
+++ b/activerecord/test/models/hotel.rb
@@ -3,5 +3,9 @@ class Hotel < ActiveRecord::Base
has_many :chefs, through: :departments
has_many :cake_designers, source_type: 'CakeDesigner', source: :employable, through: :chefs
has_many :drink_designers, source_type: 'DrinkDesigner', source: :employable, through: :chefs
+
+ has_many :chef_lists, as: :employable_list
+ has_many :mocktail_designers, through: :chef_lists, source: :employable, :source_type => "MocktailDesigner"
+
has_many :recipes, through: :chefs
end
diff --git a/activerecord/test/models/mocktail_designer.rb b/activerecord/test/models/mocktail_designer.rb
new file mode 100644
index 0000000000..77b44651a3
--- /dev/null
+++ b/activerecord/test/models/mocktail_designer.rb
@@ -0,0 +1,2 @@
+class MocktailDesigner < DrinkDesigner
+end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 701e6f45b3..101e657982 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -21,16 +21,15 @@ ActiveRecord::Schema.define do
t.index :var_binary
end
- create_table :key_tests, force: true, :options => 'ENGINE=MyISAM' do |t|
+ create_table :key_tests, force: true, options: 'ENGINE=MyISAM' do |t|
t.string :awesome
t.string :pizza
t.string :snacks
+ t.index :awesome, type: :fulltext, name: 'index_key_tests_on_awesome'
+ t.index :pizza, using: :btree, name: 'index_key_tests_on_pizza'
+ t.index :snacks, name: 'index_key_tests_on_snack'
end
- add_index :key_tests, :awesome, :type => :fulltext, :name => 'index_key_tests_on_awesome'
- add_index :key_tests, :pizza, :using => :btree, :name => 'index_key_tests_on_pizza'
- add_index :key_tests, :snacks, :name => 'index_key_tests_on_snack'
-
create_table :collation_tests, id: false, force: true do |t|
t.string :string_cs_column, limit: 1, collation: 'utf8_bin'
t.string :string_ci_column, limit: 1, collation: 'utf8_general_ci'
@@ -62,7 +61,7 @@ SQL
ActiveRecord::Base.connection.execute <<-SQL
CREATE TABLE enum_tests (
- enum_column ENUM('text','blob','tiny','medium','long','unsigned')
+ enum_column ENUM('text','blob','tiny','medium','long','unsigned','bigint')
)
SQL
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 025184f63a..b9e0706d60 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -104,6 +104,7 @@ ActiveRecord::Schema.define do
t.column :author_visibility, :integer, default: 0
t.column :illustrator_visibility, :integer, default: 0
t.column :font_size, :integer, default: 0
+ t.column :cover, :string, default: 'hard'
end
create_table :booleans, force: true do |t|
@@ -201,12 +202,11 @@ ActiveRecord::Schema.define do
t.integer :rating, default: 1
t.integer :account_id
t.string :description, default: ""
+ t.index [:firm_id, :type, :rating], name: "company_index"
+ t.index [:firm_id, :type], name: "company_partial_index", where: "rating > 10"
+ t.index :name, name: 'company_name_index', using: :btree
end
- add_index :companies, [:firm_id, :type, :rating], name: "company_index"
- add_index :companies, [:firm_id, :type], name: "company_partial_index", where: "rating > 10"
- add_index :companies, :name, name: 'company_name_index', using: :btree
-
create_table :content, force: true do |t|
t.string :title
end
@@ -303,8 +303,8 @@ ActiveRecord::Schema.define do
create_table :edges, force: true, id: false do |t|
t.column :source_id, :integer, null: false
t.column :sink_id, :integer, null: false
+ t.index [:source_id, :sink_id], unique: true, name: 'unique_edge_index'
end
- add_index :edges, [:source_id, :sink_id], unique: true, name: 'unique_edge_index'
create_table :engines, force: true do |t|
t.integer :car_id
@@ -781,8 +781,8 @@ ActiveRecord::Schema.define do
t.string :nick, null: false
t.string :name
t.column :books_count, :integer, null: false, default: 0
+ t.index :nick, unique: true
end
- add_index :subscribers, :nick, unique: true
create_table :subscriptions, force: true do |t|
t.string :subscriber_id
@@ -975,6 +975,8 @@ ActiveRecord::Schema.define do
t.integer :employable_id
t.string :employable_type
t.integer :department_id
+ t.string :employable_list_type
+ t.integer :employable_list_id
end
create_table :recipes, force: true do |t|
t.integer :chef_id