aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/relation')
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb91
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb7
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