aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb44
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/enum.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/where_clause_factory.rb1
-rw-r--r--activerecord/lib/active_record/sanitization.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb9
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb8
14 files changed, 92 insertions, 70 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 264f869c68..f35e1d889e 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2015 David Heinemeier Hansson
+# Copyright (c) 2004-2016 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index fc12c3f45a..bac5a38a5d 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -331,15 +331,30 @@ module ActiveRecord
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
unless valid = record.valid?(validation_context)
if reflection.options[:autosave]
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
+
record.errors.each do |attribute, message|
- if index.nil? || (!reflection.options[:index_errors] && !ActiveRecord::Base.index_nested_attribute_errors)
- attribute = "#{reflection.name}.#{attribute}"
- else
+ if indexed_attribute
attribute = "#{reflection.name}[#{index}].#{attribute}"
+ else
+ attribute = "#{reflection.name}.#{attribute}"
end
errors[attribute] << message
errors[attribute].uniq!
end
+
+ record.errors.details.each_key do |attribute|
+ if indexed_attribute
+ reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
+ else
+ reflection_attribute = "#{reflection.name}.#{attribute}"
+ end
+
+ record.errors.details[attribute].each do |error|
+ errors.details[reflection_attribute] << error
+ errors.details[reflection_attribute].uniq!
+ end
+ end
else
errors.add(reflection.name)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 7bf548fcba..a918a8b035 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1028,11 +1028,12 @@ module ActiveRecord
end
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
+ # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
# require the order columns appear in the SELECT.
#
# columns_for_distinct("posts.id", ["posts.created_at desc"])
- def columns_for_distinct(columns, orders) #:nodoc:
+ #
+ def columns_for_distinct(columns, orders) # :nodoc:
columns
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 3b8d1c1a9f..9cbc973f2e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -391,19 +391,17 @@ module ActiveRecord
def release_savepoint(name = nil)
end
- def case_sensitive_modifier(node, table_attribute)
- node
- end
-
def case_sensitive_comparison(table, attribute, column, value)
- table_attr = table[attribute]
- value = case_sensitive_modifier(value, table_attr) unless value.nil?
- table_attr.eq(value)
+ if value.nil?
+ table[attribute].eq(value)
+ else
+ table[attribute].eq(Arel::Nodes::BindParam.new)
+ end
end
def case_insensitive_comparison(table, attribute, column, value)
if can_perform_case_insensitive_comparison_for?(column)
- table[attribute].lower.eq(table.lower(value))
+ table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new))
else
case_sensitive_comparison(table, attribute, column, value)
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 37e4eb24a8..f5b5482efb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -623,6 +623,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by create_table.
def drop_table(table_name, options = {})
+ create_table_info_cache.delete(table_name) if create_table_info_cache.key?(table_name)
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -751,16 +752,11 @@ module ActiveRecord
SQL
end
- def case_sensitive_modifier(node, table_attribute)
- node = Arel::Nodes.build_quoted node, table_attribute
- Arel::Nodes::Bin.new(node)
- end
-
def case_sensitive_comparison(table, attribute, column, value)
- if column.case_sensitive?
- table[attribute].eq(value)
- else
+ if value.nil? || column.case_sensitive?
super
+ else
+ table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
end
end
@@ -768,10 +764,25 @@ module ActiveRecord
if column.case_sensitive?
super
else
- table[attribute].eq(value)
+ table[attribute].eq(Arel::Nodes::BindParam.new)
end
end
+ # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
+ # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
+ # distinct queries, and requires that the ORDER BY include the distinct column.
+ # See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
+ def columns_for_distinct(columns, orders) # :nodoc:
+ order_columns = orders.reject(&:blank?).map { |s|
+ # Convert Arel node to string
+ s = s.to_sql unless s.is_a?(String)
+ # Remove any ASC/DESC modifiers
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+
+ [super, *order_columns].join(', ')
+ end
+
def strict_mode?
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
end
@@ -951,11 +962,13 @@ module ActiveRecord
subsubselect = select.clone
subsubselect.projections = [key]
+ # Materialize subquery by adding distinct
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
+ subsubselect.distinct unless select.limit || select.offset || select.orders.any?
+
subselect = Arel::SelectManager.new(select.engine)
subselect.project Arel.sql(key.name)
- # Materialized subquery by adding distinct
- # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
- subselect.from subsubselect.distinct.as('__active_record_temp')
+ subselect.from subsubselect.as('__active_record_temp')
end
def mariadb?
@@ -1018,9 +1031,12 @@ module ActiveRecord
end
end
+ def create_table_info_cache # :nodoc:
+ @create_table_info_cache ||= {}
+ end
+
def create_table_info(table_name) # :nodoc:
- @create_table_info_cache = {}
- @create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
+ create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index f4686b680c..96a3a44b30 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -99,33 +99,15 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
#++
- # FIXME: re-enable the following once a "better" query_cache solution is in core
- #
- # The overrides below perform much better than the originals in AbstractAdapter
- # because we're able to take advantage of mysql2's lazy-loading capabilities
- #
- # # Returns a record hash with the column names as keys and column values
- # # as values.
- # def select_one(sql, name = nil)
- # result = execute(sql, name)
- # result.each(as: :hash) do |r|
- # return r
- # end
- # end
- #
- # # Returns a single value from a record
- # def select_value(sql, name = nil)
- # result = execute(sql, name)
- # if first = result.first
- # first.first
- # end
- # end
- #
- # # Returns an array of the values of the first column in a select:
- # # select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
- # def select_values(sql, name = nil)
- # execute(sql, name).map { |row| row.first }
- # end
+ # Returns a record hash with the column names as keys and column values
+ # as values.
+ def select_one(arel, name = nil, binds = [])
+ arel, binds = binds_from_relation(arel, binds)
+ execute(to_sql(arel, binds), name).each(as: :hash) do |row|
+ @connection.next_result while @connection.more_results?
+ return row
+ end
+ end
# Returns an array of arrays containing the field values.
# Order is the same as that returned by +columns+.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index a1ec570042..e313a34c3a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -390,12 +390,12 @@ module ActiveRecord
"average" => "avg",
}
- protected
+ # Returns the version of the connected PostgreSQL server.
+ def postgresql_version
+ @connection.server_version
+ end
- # Returns the version of the connected PostgreSQL server.
- def postgresql_version
- @connection.server_version
- end
+ protected
# See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
FOREIGN_KEY_VIOLATION = "23503"
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 7ded96f8fb..04519f4dc3 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -124,7 +124,7 @@ module ActiveRecord
def deserialize(value)
return if value.nil?
- mapping.key(value.to_i)
+ mapping.key(value)
end
def serialize(value)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index ba238ea142..8c9eab436d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -126,9 +126,9 @@ module ActiveRecord
class PendingMigrationError < MigrationError#:nodoc:
def initialize(message = nil)
if !message && defined?(Rails.env)
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}.")
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}")
elsif !message
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate.")
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate")
else
super
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 983bf019bc..b86cc094fc 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -649,8 +649,8 @@ module ActiveRecord
# they must differ only by #where (if no #group has been defined) or #having (if a #group is
# present). Neither relation may have a #limit, #offset, or #distinct set.
#
- # Post.where("id = 1").or(Post.where("id = 2"))
- # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
+ # Post.where("id = 1").or(Post.where("author_id = 3"))
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3'))
#
def or(other)
spawn.or!(other)
diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb
index a81ff98e49..dbf172a577 100644
--- a/activerecord/lib/active_record/relation/where_clause_factory.rb
+++ b/activerecord/lib/active_record/relation/where_clause_factory.rb
@@ -22,6 +22,7 @@ module ActiveRecord
parts = predicate_builder.build_from_hash(attributes)
when Arel::Nodes::Node
parts = [opts]
+ binds = other
else
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 4e89ba4dd1..2bfc5ff7ae 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -213,7 +213,7 @@ module ActiveRecord
end
# TODO: Deprecate this
- def quoted_id
+ def quoted_id # :nodoc:
self.class.quote_value(@attributes[self.class.primary_key].value_for_database)
end
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index edc1325b25..a376e2a17f 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -73,15 +73,18 @@ module ActiveRecord
value = value.to_s[0, column.limit]
end
- value = Arel::Nodes::Quoted.new(value)
-
comparison = if !options[:case_sensitive] && !value.nil?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
klass.connection.case_sensitive_comparison(table, attribute, column, value)
end
- klass.unscoped.where(comparison)
+ if value.nil?
+ klass.unscoped.where(comparison)
+ else
+ bind = Relation::QueryAttribute.new(attribute.to_s, value, Type::Value.new)
+ klass.unscoped.where(comparison, bind)
+ end
rescue RangeError
klass.none
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index 15aecf28ca..7395839fca 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -45,7 +45,13 @@ module ActiveRecord
def determine_default_parent_class
application_record = nil
- in_root { application_record = File.exist?('app/models/application_record.rb') }
+ in_root do
+ application_record = if mountable_engine?
+ File.exist?("app/models/#{namespaced_path}/application_record.rb")
+ else
+ File.exist?('app/models/application_record.rb')
+ end
+ end
if application_record
"ApplicationRecord"