diff options
Diffstat (limited to 'activerecord/lib/active_record/relation/query_methods.rb')
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 130 |
1 files changed, 55 insertions, 75 deletions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index c34e4bfb9b..6d300372cb 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,6 +1,9 @@ +require "active_record/relation/from_clause" +require "active_record/relation/where_clause" +require "active_record/relation/where_clause_factory" +require 'active_model/forbidden_attributes_protection' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/string/filters' -require 'active_model/forbidden_attributes_protection' module ActiveRecord module QueryMethods @@ -39,23 +42,10 @@ module ActiveRecord # User.where.not(name: "Jon", role: "admin") # # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin' def not(opts, *rest) - where_value = @scope.send(:build_where, opts, rest).map do |rel| - case rel - when NilClass - raise ArgumentError, 'Invalid argument for .where.not(), got nil.' - when Arel::Nodes::In - Arel::Nodes::NotIn.new(rel.left, rel.right) - when Arel::Nodes::Equality - Arel::Nodes::NotEqual.new(rel.left, rel.right) - when String - Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel)) - else - Arel::Nodes::Not.new(rel) - end - end + where_clause = @scope.send(:where_clause_factory).build(opts, rest) @scope.references!(PredicateBuilder.references(opts)) if Hash === opts - @scope.where_values += where_value + @scope.where_clause += where_clause.invert @scope end end @@ -90,6 +80,23 @@ module ActiveRecord CODE end + Relation::CLAUSE_METHODS.each do |name| + class_eval <<-CODE, __FILE__, __LINE__ + 1 + def #{name}_clause # def where_clause + @values[:#{name}] || new_#{name}_clause # @values[:where] || new_where_clause + end # end + # + def #{name}_clause=(value) # def where_clause=(value) + assert_mutability! # assert_mutability! + @values[:#{name}] = value # @values[:where] = value + end # end + CODE + end + + def bind_values + from_clause.binds + where_clause.binds + having_clause.binds + end + def create_with_value # :nodoc: @values[:create_with] || {} end @@ -392,9 +399,8 @@ module ActiveRecord raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key." end - Array(target_value).each do |val| - where_unscoping(val) - end + target_values = Array(target_value).map(&:to_s) + self.where_clause = where_clause.except(*target_values) end else raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example." @@ -425,15 +431,6 @@ module ActiveRecord self end - def bind(value) # :nodoc: - spawn.bind!(value) - end - - def bind!(value) # :nodoc: - self.bind_values += [value] - self - end - # Returns a new relation, which is the result of filtering the current relation # according to the conditions in the arguments. # @@ -569,7 +566,7 @@ module ActiveRecord references!(PredicateBuilder.references(opts)) end - self.where_values += build_where(opts, rest) + self.where_clause += where_clause_factory.build(opts, rest) self end @@ -596,7 +593,7 @@ module ActiveRecord def having!(opts, *rest) # :nodoc: references!(PredicateBuilder.references(opts)) if Hash === opts - self.having_values += build_where(opts, rest) + self.having_clause += having_clause_factory.build(opts, rest) self end @@ -744,10 +741,7 @@ module ActiveRecord end def from!(value, subquery_name = nil) # :nodoc: - self.from_value = [value, subquery_name] - if value.is_a? Relation - self.bind_values = value.arel.bind_values + value.bind_values + bind_values - end + self.from_clause = Relation::FromClause.new(value, subquery_name) self end @@ -858,9 +852,9 @@ module ActiveRecord build_joins(arel, joins_values.flatten) unless joins_values.empty? - collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds + collapse_wheres(arel, (where_clause.predicates - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds - arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty? + arel.having(*having_clause.predicates.uniq.reject(&:blank?)) if having_clause.any? arel.take(connection.sanitize_limit(limit_value)) if limit_value arel.skip(offset_value.to_i) if offset_value @@ -872,7 +866,7 @@ module ActiveRecord build_select(arel, select_values.uniq) arel.distinct(distinct_value) - arel.from(build_from) if from_value + arel.from(build_from) unless from_clause.empty? arel.lock(lock_value) if lock_value arel @@ -883,35 +877,24 @@ module ActiveRecord raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}." end - single_val_method = Relation::SINGLE_VALUE_METHODS.include?(scope) - unscope_code = "#{scope}_value#{'s' unless single_val_method}=" + clause_method = Relation::CLAUSE_METHODS.include?(scope) + multi_val_method = Relation::MULTI_VALUE_METHODS.include?(scope) + if clause_method + unscope_code = "#{scope}_clause=" + else + unscope_code = "#{scope}_value#{'s' if multi_val_method}=" + end case scope when :order result = [] - when :where - self.bind_values = [] else - result = [] unless single_val_method + result = [] if multi_val_method end self.send(unscope_code, result) end - def where_unscoping(target_value) - target_value = target_value.to_s - - where_values.reject! do |rel| - case rel - when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual - subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right) - subrelation.name.to_s == target_value - end - end - - self.bind_values = bind_values.reject { |col,_| col.name == target_value } - end - def custom_join_ast(table, joins) joins = joins.reject(&:blank?) @@ -938,24 +921,6 @@ module ActiveRecord arel.where(Arel::Nodes::And.new(predicates)) if predicates.present? end - def build_where(opts, other = []) - case opts - when String, Array - [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] - when Hash - opts = predicate_builder.resolve_column_aliases(opts) - - tmp_opts, bind_values = predicate_builder.create_binds(opts) - self.bind_values += bind_values - - attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts) - - predicate_builder.build_from_hash(attributes) - else - [opts] - end - end - def association_for_table(table_name) table_name = table_name.to_s @klass._reflect_on_association(table_name) || @@ -963,7 +928,8 @@ module ActiveRecord end def build_from - opts, name = from_value + opts = from_clause.value + name = from_clause.name case opts when Relation name ||= 'subquery' @@ -1118,5 +1084,19 @@ module ActiveRecord raise ArgumentError, "The method .#{method_name}() must contain arguments." end end + + def new_where_clause + Relation::WhereClause.empty + end + alias new_having_clause new_where_clause + + def where_clause_factory + @where_clause_factory ||= Relation::WhereClauseFactory.new(klass, predicate_builder) + end + alias having_clause_factory where_clause_factory + + def new_from_clause + Relation::FromClause.empty + end end end |