aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency.rb37
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb26
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb2
-rw-r--r--activerecord/lib/active_record/base.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb65
-rw-r--r--activerecord/lib/active_record/transactions.rb3
7 files changed, 83 insertions, 56 deletions
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
index c578845878..13576e1aec 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
@@ -6,14 +6,17 @@ module ActiveRecord
module Associations
module ClassMethods
class JoinDependency # :nodoc:
- attr_reader :join_parts, :reflections, :table_aliases
+ attr_reader :join_parts, :reflections, :table_aliases, :active_record
def initialize(base, associations, joins)
- @table_joins = joins || ''
+ @active_record = base
+ @table_joins = joins
@join_parts = [JoinBase.new(base)]
@associations = {}
@reflections = []
- @table_aliases = Hash.new(0)
+ @table_aliases = Hash.new do |h,name|
+ h[name] = count_aliases_from_table_joins(name)
+ end
@table_aliases[base.table_name] = 1
build(associations)
end
@@ -44,12 +47,26 @@ module ActiveRecord
end
def count_aliases_from_table_joins(name)
+ return 0 if !@table_joins || Arel::Table === @table_joins
+
+ @table_joins.grep(Arel::Nodes::Join).map { |join|
+ right = join.right
+ case right
+ when Arel::Table
+ right.name.downcase == name ? 1 : 0
+ when String
+ count_aliases_from_string(right.downcase, name)
+ else
+ 0
+ end
+ }.sum
+ end
+
+ def count_aliases_from_string(join_sql, name)
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
- quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase
- join_sql = @table_joins.downcase
- join_sql.blank? ? 0 :
- # Table names
- join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
+ quoted_name = active_record.connection.quote_table_name(name.downcase).downcase
+ # Table names
+ join_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size +
# Table aliases
join_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
end
@@ -61,11 +78,11 @@ module ActiveRecord
records = rows.map { |model|
primary_id = model[primary_key]
parent = parents[primary_id] ||= join_base.instantiate(model)
- construct(parent, @associations, join_associations.dup, model)
+ construct(parent, @associations, join_associations, model)
parent
}.uniq
- remove_duplicate_results!(join_base.active_record, records, @associations)
+ remove_duplicate_results!(active_record, records, @associations)
records
end
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
index 4dd11a7366..25511a092f 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
@@ -67,8 +67,7 @@ module ActiveRecord
def table
@table ||= Arel::Table.new(
- table_name, :as => aliased_table_name,
- :engine => arel_engine, :columns => active_record.columns
+ table_name, :as => aliased_table_name, :engine => arel_engine
)
end
@@ -78,23 +77,18 @@ module ActiveRecord
protected
def aliased_table_name_for(name, suffix = nil)
- if @join_dependency.table_aliases[name].zero?
- @join_dependency.table_aliases[name] = @join_dependency.count_aliases_from_table_joins(name)
- end
+ aliases = @join_dependency.table_aliases
- if !@join_dependency.table_aliases[name].zero? # We need an alias
- name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
- @join_dependency.table_aliases[name] += 1
- if @join_dependency.table_aliases[name] == 1 # First time we've seen this name
- # Also need to count the aliases from the table_aliases to avoid incorrect count
- @join_dependency.table_aliases[name] += @join_dependency.count_aliases_from_table_joins(name)
- end
- table_index = @join_dependency.table_aliases[name]
- name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
- else
- @join_dependency.table_aliases[name] += 1
+ if aliases[name] != 0 # We need an alias
+ connection = active_record.connection
+
+ name = connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
+ table_index = aliases[name] + 1
+ name = name[0, connection.table_alias_length-3] + "_#{table_index}" if table_index > 1
end
+ aliases[name] += 1
+
name
end
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
index 97003c1457..67567f06df 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
@@ -13,7 +13,7 @@ module ActiveRecord
end
def table
- Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns)
+ Arel::Table.new(table_name, arel_engine)
end
def aliased_table_name
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index ba7f6f0129..b55aaddbd7 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1500,9 +1500,7 @@ MSG
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
def attributes
- attrs = {}
- attribute_names.each { |name| attrs[name] = read_attribute(name) }
- attrs
+ Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
end
# Returns an <tt>#inspect</tt>-like string for the value of the
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 51ba17c9be..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.join_sql)
+ 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
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 0483950db7..6886c7538b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -162,27 +162,6 @@ module ActiveRecord
@arel ||= build_arel
end
- def custom_join_sql(joins)
- arel = table.select_manager
-
- joins.each do |join|
- next if join.blank?
-
- @implicit_readonly = true
-
- case join
- when Array
- join = Arel.sql(join.join(' ')) if array_of_strings?(join)
- when String
- join = Arel.sql(join)
- end
-
- arel.join(join)
- end
-
- arel.join_sql
- end
-
def build_arel
arel = table
@@ -209,6 +188,32 @@ module ActiveRecord
private
+ def custom_join_ast(table, joins)
+ joins = joins.reject { |join| join.blank? }
+
+ return if joins.empty?
+
+ @implicit_readonly = true
+
+ 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
+
+ head = table.create_string_join(table, joins.shift)
+
+ 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)
@@ -252,19 +257,31 @@ module ActiveRecord
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(relation, 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)
end
- relation.join(custom_joins)
+ if Arel::Table === relation
+ relation.from(join_ast || relation)
+ else
+ if relation.froms.length > 0 && join_ast
+ join_ast.left = relation.froms.first
+ relation.from join_ast
+ elsif relation.froms.length == 0 && join_ast
+ relation.from(join_ast)
+ else
+ relation
+ end
+ end
end
def build_select(arel, selects)
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 654c475aed..181280baaa 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -141,7 +141,8 @@ module ActiveRecord
# end
# end
#
- # User.find(:all) # => empty
+ # User.find(:all) # => Return both Kotori and Nemu, because inner transaction do not rollback
+ # # without :requiers_new => true option, and Rollback exception do not reraise
#
# It is also possible to requires a sub-transaction by passing
# <tt>:requires_new => true</tt>. If anything goes wrong, the