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.rb17
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb143
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb7
5 files changed, 98 insertions, 73 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 4192456447..8bc28c06cb 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -187,7 +187,7 @@ module ActiveRecord
def find_with_associations
including = (@eager_load_values + @includes_values).uniq
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, [])
rows = construct_relation_for_association_find(join_dependency).to_a
join_dependency.instantiate(rows)
rescue ThrowResult
@@ -196,13 +196,13 @@ 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
def construct_relation_for_association_find(join_dependency)
- relation = except(:includes, :eager_load, :preload, :select).select(column_aliases(join_dependency))
+ relation = except(:includes, :eager_load, :preload, :select).select(join_dependency.columns)
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))
@@ -349,17 +349,8 @@ module ActiveRecord
end
end
- def column_aliases(join_dependency)
- join_dependency.join_parts.collect { |join_part|
- join_part.column_names_with_alias.collect{ |column_name, aliased_name|
- "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"
- }
- }.flatten.join(", ")
- end
-
def using_limitable_reflections?(reflections)
reflections.none? { |r| r.collection? }
end
-
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 32c7d08daa..70d84619a1 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -26,7 +26,7 @@ module ActiveRecord
when Range, Arel::Relation
attribute.in(value)
when ActiveRecord::Base
- attribute.eq(value.quoted_id)
+ attribute.eq(Arel.sql(value.quoted_id))
when Class
# FIXME: I think we need to deprecate this behavior
attribute.eq(value.name)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9e7503a60d..0ab55ae864 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -13,7 +13,7 @@ module ActiveRecord
def includes(*args)
args.reject! {|a| a.blank? }
- return clone if args.empty?
+ return self if args.empty?
relation = clone
relation.includes_values = (relation.includes_values + args).flatten.uniq
@@ -21,14 +21,18 @@ module ActiveRecord
end
def eager_load(*args)
+ return self if args.blank?
+
relation = clone
- relation.eager_load_values += args unless args.blank?
+ relation.eager_load_values += args
relation
end
def preload(*args)
+ return self if args.blank?
+
relation = clone
- relation.preload_values += args unless args.blank?
+ relation.preload_values += args
relation
end
@@ -43,22 +47,28 @@ module ActiveRecord
end
def group(*args)
+ return self if args.blank?
+
relation = clone
- relation.group_values += args.flatten unless args.blank?
+ relation.group_values += args.flatten
relation
end
def order(*args)
+ return self if args.blank?
+
relation = clone
- relation.order_values += args.flatten unless args.blank?
+ relation.order_values += args.flatten
relation
end
def joins(*args)
+ return self if args.compact.blank?
+
relation = clone
args.flatten!
- relation.joins_values += args unless args.blank?
+ relation.joins_values += args
relation
end
@@ -70,14 +80,18 @@ module ActiveRecord
end
def where(opts, *rest)
+ return self if opts.blank?
+
relation = clone
- relation.where_values += build_where(opts, rest) unless opts.blank?
+ relation.where_values += build_where(opts, rest)
relation
end
def having(*args)
+ return self if args.blank?
+
relation = clone
- relation.having_values += build_where(*args) unless args.blank?
+ relation.having_values += build_where(*args)
relation
end
@@ -148,53 +162,50 @@ module ActiveRecord
@arel ||= build_arel
end
- def custom_join_sql(*joins)
- arel = table.select_manager
-
- joins.each do |join|
- next if join.blank?
+ def build_arel
+ arel = table.from table
- @implicit_readonly = true
+ build_joins(arel, @joins_values) unless @joins_values.empty?
- case join
- when Array
- join = Arel.sql(join.join(' ')) if array_of_strings?(join)
- when String
- join = Arel.sql(join)
- end
+ collapse_wheres(arel, (@where_values - ['']).uniq)
- arel.join(join)
- end
+ arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
- arel.joins(arel)
- end
+ arel.take(@limit_value) if @limit_value
+ arel.skip(@offset_value) if @offset_value
- def build_arel
- arel = table
+ arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
- arel = build_joins(arel, @joins_values) unless @joins_values.empty?
+ arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
- arel = collapse_wheres(arel, (@where_values - ['']).uniq)
+ build_select(arel, @select_values.uniq)
- arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
+ arel.from(@from_value) if @from_value
+ arel.lock(@lock_value) if @lock_value
- arel = arel.take(@limit_value) if @limit_value
- arel = arel.skip(@offset_value) if @offset_value
+ arel
+ end
- arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
+ private
- arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
+ def custom_join_ast(table, joins)
+ joins = joins.reject { |join| join.blank? }
- arel = build_select(arel, @select_values.uniq)
+ return [] if joins.empty?
- arel = arel.from(@from_value) if @from_value
- arel = arel.lock(@lock_value) if @lock_value
+ @implicit_readonly = true
- arel
+ 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
+ table.create_string_join(join)
+ end
end
- private
-
def collapse_wheres(arel, wheres)
equalities = wheres.grep(Arel::Nodes::Equality)
@@ -206,14 +217,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 = [])
@@ -228,31 +238,54 @@ module ActiveRecord
end
end
- def build_joins(relation, joins)
- association_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)
+ def build_joins(manager, joins)
+ buckets = joins.group_by do |join|
+ case join
+ when String
+ 'string_join'
+ when Hash, Symbol, Array
+ 'association_join'
+ when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
+ 'stashed_join'
+ when Arel::Nodes::Join
+ 'join_node'
+ else
+ raise 'unknown class: %s' % join.class.name
+ end
end
- stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
+ association_joins = buckets['association_join'] || []
+ stashed_association_joins = buckets['stashed_join'] || []
+ join_nodes = buckets['join_node'] || []
+ string_joins = (buckets['string_join'] || []).map { |x|
+ x.strip
+ }.uniq
- non_association_joins = (joins - association_joins - stashed_association_joins)
- custom_joins = custom_join_sql(*non_association_joins)
+ join_list = custom_join_ast(manager, string_joins)
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(
+ @klass,
+ association_joins,
+ join_list
+ )
+
+ join_nodes.each do |join|
+ join_dependency.table_aliases[join.left.name.downcase] = 1
+ end
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)
+ manager.join_sources.concat join_nodes.uniq
+ manager.join_sources.concat join_list
+
+ 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