diff options
Diffstat (limited to 'activerecord/lib/active_record')
7 files changed, 78 insertions, 20 deletions
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index a6d81c82b4..4c22be8235 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -29,14 +29,6 @@ module ActiveRecord assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty? end - # Tries to assign given value to given attribute. - # In case of an error, re-raises with the ActiveRecord constant. - def _assign_attribute(k, v) # :nodoc: - super - rescue ActiveModel::UnknownAttributeError - raise UnknownAttributeError.new(self, k) - end - # Assign any deferred nested attributes after the base attributes have been set. def assign_nested_parameter_attributes(pairs) pairs.each { |k, v| _assign_attribute(k, v) } diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d9b42d4283..5ef434734a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -1,5 +1,4 @@ require 'active_record/type' -require 'active_support/core_ext/benchmark' require 'active_record/connection_adapters/determine_if_preparable_visitor' require 'active_record/connection_adapters/schema_cache' require 'active_record/connection_adapters/sql_type_metadata' @@ -398,7 +397,7 @@ module ActiveRecord if can_perform_case_insensitive_comparison_for?(column) table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new)) else - case_sensitive_comparison(table, attribute, column, value) + table[attribute].eq(Arel::Nodes::BindParam.new) end 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 8751b6da4b..b12bac2737 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -615,13 +615,10 @@ module ActiveRecord end end - def case_insensitive_comparison(table, attribute, column, value) - if column.case_sensitive? - super - else - table[attribute].eq(Arel::Nodes::BindParam.new) - end + def can_perform_case_insensitive_comparison_for?(column) + column.case_sensitive? end + private :can_perform_case_insensitive_comparison_for? # 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 diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 0f88791d92..953495a8b6 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -5,6 +5,7 @@ module ActiveRecord require 'active_record/relation/predicate_builder/base_handler' require 'active_record/relation/predicate_builder/basic_object_handler' require 'active_record/relation/predicate_builder/class_handler' + require 'active_record/relation/predicate_builder/polymorphic_array_handler' require 'active_record/relation/predicate_builder/range_handler' require 'active_record/relation/predicate_builder/relation_handler' @@ -22,6 +23,7 @@ module ActiveRecord register_handler(Relation, RelationHandler.new) register_handler(Array, ArrayHandler.new(self)) register_handler(AssociationQueryValue, AssociationQueryHandler.new(self)) + register_handler(PolymorphicArrayValue, PolymorphicArrayHandler.new(self)) end def build_from_hash(attributes) @@ -40,10 +42,7 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - if table.associated_with?(column) - value = AssociationQueryValue.new(table.associated_table(column), value) - end - + value = AssociationQueryHandler.value_for(table, column, value) if table.associated_with?(column) build(table.arel_attribute(column), value) end diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb index e81be63cd3..d7fd878265 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb @@ -1,6 +1,16 @@ module ActiveRecord class PredicateBuilder class AssociationQueryHandler # :nodoc: + def self.value_for(table, column, value) + klass = if table.associated_table(column).polymorphic_association? && ::Array === value && value.first.is_a?(Base) + PolymorphicArrayValue + else + AssociationQueryValue + end + + klass.new(table.associated_table(column), value) + end + def initialize(predicate_builder) @predicate_builder = predicate_builder end diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb new file mode 100644 index 0000000000..b6c6240343 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb @@ -0,0 +1,57 @@ +module ActiveRecord + class PredicateBuilder + class PolymorphicArrayHandler # :nodoc: + def initialize(predicate_builder) + @predicate_builder = predicate_builder + end + + def call(attribute, value) + table = value.associated_table + queries = value.type_to_ids_mapping.map do |type, ids| + { table.association_foreign_type.to_s => type, table.association_foreign_key.to_s => ids } + end + + predicates = queries.map { |query| predicate_builder.build_from_hash(query) } + + if predicates.size > 1 + type_and_ids_predicates = predicates.map { |type_predicate, id_predicate| Arel::Nodes::Grouping.new(type_predicate.and(id_predicate)) } + type_and_ids_predicates.inject(&:or) + else + predicates.first + end + end + + protected + + attr_reader :predicate_builder + end + + class PolymorphicArrayValue # :nodoc: + attr_reader :associated_table, :values + + def initialize(associated_table, values) + @associated_table = associated_table + @values = values + end + + def type_to_ids_mapping + default_hash = Hash.new { |hsh, key| hsh[key] = [] } + values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) } + end + + private + + def primary_key(value) + associated_table.association_primary_key(base_class(value)) + end + + def base_class(value) + value.class.base_class + end + + def convert_to_id(value) + value._read_attribute(primary_key(value)) + end + end + end +end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 91d486e902..4533f3263f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -655,6 +655,10 @@ module ActiveRecord # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3')) # def or(other) + unless other.is_a? Relation + raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead." + end + spawn.or!(other) end |