diff options
Diffstat (limited to 'activerecord/lib/active_record/relation')
4 files changed, 56 insertions, 48 deletions
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c8adaddfca..fd45bb24dd 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -166,7 +166,7 @@ module ActiveRecord if operation == "count" column_name ||= (select_for_count || :all) - if arel.joins(arel) =~ /LEFT OUTER/i + unless arel.ast.grep(Arel::Nodes::OuterJoin).empty? distinct = true column_name = @klass.primary_key if column_name == :all end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 23ae0b4325..906ad7699c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -196,7 +196,7 @@ module ActiveRecord def construct_relation_for_association_calculations including = (@eager_load_values + @includes_values).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel)) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.froms.first) relation = except(:includes, :eager_load, :preload) apply_join_dependency(relation, join_dependency) end @@ -290,7 +290,7 @@ module ActiveRecord def find_one(id) id = id.id if ActiveRecord::Base === id - column = primary_key.column + column = columns_hash[primary_key.name.to_s] substitute = connection.substitute_for(column, @bind_values) relation = where(primary_key.eq(substitute)) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0a4c119849..51a39be065 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -162,52 +162,57 @@ module ActiveRecord @arel ||= build_arel end - def custom_join_sql(*joins) - arel = table.select_manager + def build_arel + arel = table.from table - joins.each do |join| - next if join.blank? + build_joins(arel, @joins_values) unless @joins_values.empty? - @implicit_readonly = true + collapse_wheres(arel, (@where_values - ['']).uniq) - case join - when Array - join = Arel.sql(join.join(' ')) if array_of_strings?(join) - when String - join = Arel.sql(join) - end + arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? - arel.join(join) - end + arel.take(@limit_value) if @limit_value + arel.skip(@offset_value) if @offset_value - arel.joins(arel) - end + arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? - def build_arel - arel = table + arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? - arel = build_joins(arel, @joins_values) unless @joins_values.empty? + build_select(arel, @select_values.uniq) - arel = collapse_wheres(arel, (@where_values - ['']).uniq) + arel.from(@from_value) if @from_value + arel.lock(@lock_value) if @lock_value - arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? + arel + end - arel = arel.take(@limit_value) if @limit_value - arel = arel.skip(@offset_value) if @offset_value + private - arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? + def custom_join_ast(table, joins) + joins = joins.reject { |join| join.blank? } - arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty? + return if joins.empty? - arel = build_select(arel, @select_values.uniq) + @implicit_readonly = true - arel = arel.from(@from_value) if @from_value - arel = arel.lock(@lock_value) if @lock_value + joins.map! do |join| + case join + when Array + join = Arel.sql(join.join(' ')) if array_of_strings?(join) + when String + join = Arel.sql(join) + end + join + end - arel - end + head = table.create_string_join(table, joins.shift) - private + joins.inject(head) do |ast, join| + ast.right = table.create_string_join(ast.right, join) + end + + head + end def collapse_wheres(arel, wheres) equalities = wheres.grep(Arel::Nodes::Equality) @@ -220,14 +225,13 @@ module ActiveRecord test = eqls.inject(eqls.shift) do |memo, expr| memo.or(expr) end - arel = arel.where(test) + arel.where(test) end (wheres - equalities).each do |where| where = Arel.sql(where) if String === where - arel = arel.where(Arel::Nodes::Grouping.new(where)) + arel.where(Arel::Nodes::Grouping.new(where)) end - arel end def build_where(opts, other = []) @@ -242,31 +246,34 @@ module ActiveRecord end end - def build_joins(relation, joins) - association_joins = [] - + def build_joins(manager, joins) joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq - joins.each do |join| - association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) + association_joins = joins.find_all do |join| + [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join) end stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation) non_association_joins = (joins - association_joins - stashed_association_joins) - custom_joins = custom_join_sql(*non_association_joins) + join_ast = custom_join_ast(manager.froms.first, non_association_joins) - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast) join_dependency.graft(*stashed_association_joins) @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? + # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| - relation = association.join_to(relation) + association.join_to(manager) end - relation.join(custom_joins) + return manager unless join_ast + + join_ast.left = manager.froms.first + manager.from join_ast + manager end def build_select(arel, selects) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a61a3bd41c..5acf3ec83a 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -3,10 +3,11 @@ require 'active_support/core_ext/object/blank' module ActiveRecord module SpawnMethods def merge(r) - merged_relation = clone - return merged_relation unless r + return self unless r return to_a & r if r.is_a?(Array) + merged_relation = clone + Relation::ASSOCIATION_METHODS.each do |method| value = r.send(:"#{method}_values") @@ -24,7 +25,7 @@ module ActiveRecord merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present? end - merged_relation = merged_relation.joins(r.joins_values) + merged_relation.joins_values += r.joins_values merged_wheres = @where_values + r.where_values |