mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
finally fixed table aliasing issues
- the solution is currently ugly, but i have an idea how to clean it up
This commit is contained in:
parent
6c61ebb4d6
commit
85bc3b417d
6 changed files with 29 additions and 37 deletions
22
doc/TODO
22
doc/TODO
|
@ -1,24 +1,14 @@
|
||||||
todo:
|
todo:
|
||||||
|
- Explicitly model recursive structural decomposition / polymorphism
|
||||||
|
- Explicitly model the namer/externalizer using interpreter jargon
|
||||||
|
- All Sql Strategies should be accumulations with the top-level relation?
|
||||||
- lock
|
- lock
|
||||||
- SELECT suchandsuch FOR UPDATE
|
- SELECT suchandsuch FOR UPDATE
|
||||||
- anonymous table names
|
|
||||||
- rename select to where
|
- rename select to where
|
||||||
- and/or w/ predicates
|
- and/or w/ predicates
|
||||||
- audit active record quoting
|
|
||||||
- limit clauses with left outer joins ?
|
|
||||||
- see add_limited_ids_condition
|
|
||||||
- select_limited_ids_list
|
|
||||||
- extract adapters
|
|
||||||
- arbitrary sql relationships
|
|
||||||
- consider this code from has_many:
|
|
||||||
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
|
||||||
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
|
||||||
- can join with this as a subselect no problem right? it's' unclear what attributes it has, though.
|
|
||||||
- cache expiry on write
|
- cache expiry on write
|
||||||
- rewrite of arecord querycache test in light of this
|
- rewrite of arecord querycache test in light of this
|
||||||
- linq functionality
|
- scoped writes
|
||||||
- reverse
|
|
||||||
- any/all
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
- mock out database
|
- mock out database
|
||||||
|
@ -74,6 +64,7 @@ done:
|
||||||
- rename active_relation to arel
|
- rename active_relation to arel
|
||||||
- fix complex joining cases:
|
- fix complex joining cases:
|
||||||
- active record query adapter
|
- active record query adapter
|
||||||
|
- anonymous table names
|
||||||
|
|
||||||
icebox:
|
icebox:
|
||||||
- #bind in Attribute and Expression should be doing a descend?
|
- #bind in Attribute and Expression should be doing a descend?
|
||||||
|
@ -83,3 +74,6 @@ icebox:
|
||||||
- "unit" test sql strategies
|
- "unit" test sql strategies
|
||||||
- use real world examples, so they should be like a tutorial.
|
- use real world examples, so they should be like a tutorial.
|
||||||
- rename the tion (Selection) classes so that words that don't end in tion don't seem inconsistent
|
- rename the tion (Selection) classes so that words that don't end in tion don't seem inconsistent
|
||||||
|
- consider this code from has_many:
|
||||||
|
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
||||||
|
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
|
@ -6,7 +6,7 @@ module Arel
|
||||||
|
|
||||||
delegate :joins, :selects, :orders, :groupings, :inserts, :taken,
|
delegate :joins, :selects, :orders, :groupings, :inserts, :taken,
|
||||||
:skipped, :name, :alias, :aggregation?, :prefix_for, :column_for,
|
:skipped, :name, :alias, :aggregation?, :prefix_for, :column_for,
|
||||||
:engine,
|
:engine, :name_for,
|
||||||
:to => :relation
|
:to => :relation
|
||||||
|
|
||||||
def attributes
|
def attributes
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
module Arel
|
module Arel
|
||||||
# TODO Explicitly model recursive structural decomposition / polymorphism
|
|
||||||
# TODO Explicitly model the namer/externalizer using interpreter jargon
|
|
||||||
class Join < Relation
|
class Join < Relation
|
||||||
attr_reader :join_sql, :relation1, :relation2, :predicates
|
attr_reader :join_sql, :relation1, :relation2, :predicates
|
||||||
|
|
||||||
|
@ -26,13 +24,7 @@ module Arel
|
||||||
end
|
end
|
||||||
|
|
||||||
def prefix_for(attribute)
|
def prefix_for(attribute)
|
||||||
externalize(relation_for(attribute)).table_sql # externalize or something?
|
externalize(relation_for(attribute)).prefix_for(attribute)
|
||||||
end
|
|
||||||
|
|
||||||
def relation_for(attribute)
|
|
||||||
[relation1[attribute], relation2[attribute]].select { |a| a =~ attribute }.min do |a1, a2|
|
|
||||||
(attribute % a1).size <=> (attribute % a2).size
|
|
||||||
end.relation.relation_for(attribute)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# TESTME: Not sure which scenario needs this method, was driven by failing tests in ActiveRecord
|
# TESTME: Not sure which scenario needs this method, was driven by failing tests in ActiveRecord
|
||||||
|
@ -45,14 +37,9 @@ module Arel
|
||||||
join_sql,
|
join_sql,
|
||||||
externalize(relation2).table_sql(formatter),
|
externalize(relation2).table_sql(formatter),
|
||||||
("ON" unless predicates.blank?),
|
("ON" unless predicates.blank?),
|
||||||
predicates.collect { |p| p.bind(self).to_sql }.join(' AND ')
|
predicates.collect { |p| p.bind(formatter.christener).to_sql }.join(' AND ')
|
||||||
].compact.join(" ")
|
].compact.join(" ")
|
||||||
[relation1.joins(formatter), relation2.joins(formatter), this_join].compact.join(" ")
|
[relation1.joins(formatter), this_join, relation2.joins(formatter)].compact.join(" ")
|
||||||
end
|
|
||||||
|
|
||||||
# FIXME
|
|
||||||
def name
|
|
||||||
'user'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def selects
|
def selects
|
||||||
|
@ -72,6 +59,13 @@ module Arel
|
||||||
@relation_names[relation]
|
@relation_names[relation]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
def relation_for(attribute)
|
||||||
|
[relation1[attribute], relation2[attribute]].select { |a| a =~ attribute }.min do |a1, a2|
|
||||||
|
(attribute % a1).size <=> (attribute % a2).size
|
||||||
|
end.relation.relation_for(attribute)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def externalize(relation)
|
def externalize(relation)
|
||||||
Externalizer.new(self, relation)
|
Externalizer.new(self, relation)
|
||||||
|
@ -82,10 +76,8 @@ module Arel
|
||||||
|
|
||||||
def table_sql(formatter = Sql::TableReference.new(self))
|
def table_sql(formatter = Sql::TableReference.new(self))
|
||||||
if relation.aggregation?
|
if relation.aggregation?
|
||||||
relation.to_sql(formatter) + ' AS ' + engine.quote_table_name(christener.name_for(relation) + '_aggregation')
|
relation.to_sql(formatter) + ' AS ' + engine.quote_table_name(formatter.name_for(relation) + (relation.aggregation?? '_aggregation' : ''))
|
||||||
else
|
else
|
||||||
# not an aggregation
|
|
||||||
# all this can be is a join or a compound or a table
|
|
||||||
relation.table_sql(formatter)
|
relation.table_sql(formatter)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -97,6 +89,10 @@ module Arel
|
||||||
def attributes
|
def attributes
|
||||||
relation.aggregation?? relation.attributes.collect(&:to_attribute) : relation.attributes
|
relation.aggregation?? relation.attributes.collect(&:to_attribute) : relation.attributes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prefix_for(attribute)
|
||||||
|
christener.name_for(relation) + (relation.aggregation?? '_aggregation' : '')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -115,10 +115,11 @@ module Arel
|
||||||
include Externalizable
|
include Externalizable
|
||||||
|
|
||||||
def to_sql(formatter = Sql::SelectStatement.new(engine))
|
def to_sql(formatter = Sql::SelectStatement.new(engine))
|
||||||
|
tr = Sql::TableReference.new(self)
|
||||||
formatter.select [
|
formatter.select [
|
||||||
"SELECT #{attributes.collect { |a| a.to_sql(Sql::SelectClause.new(engine)) }.join(', ')}",
|
"SELECT #{attributes.collect { |a| a.to_sql(Sql::SelectClause.new(engine)) }.join(', ')}",
|
||||||
"FROM #{table_sql(Sql::TableReference.new(self))}",
|
"FROM #{table_sql(tr)}",
|
||||||
(joins(Sql::TableReference.new(self)) unless joins.blank? ),
|
(joins(tr) unless joins.blank? ),
|
||||||
("WHERE #{selects.collect { |s| s.to_sql(Sql::WhereClause.new(engine)) }.join("\n\tAND ")}" unless selects.blank? ),
|
("WHERE #{selects.collect { |s| s.to_sql(Sql::WhereClause.new(engine)) }.join("\n\tAND ")}" unless selects.blank? ),
|
||||||
("ORDER BY #{orders.collect { |o| o.to_sql(Sql::OrderClause.new(engine)) }.join(', ')}" unless orders.blank? ),
|
("ORDER BY #{orders.collect { |o| o.to_sql(Sql::OrderClause.new(engine)) }.join(', ')}" unless orders.blank? ),
|
||||||
("GROUP BY #{groupings.collect(&:to_sql)}" unless groupings.blank? ),
|
("GROUP BY #{groupings.collect(&:to_sql)}" unless groupings.blank? ),
|
||||||
|
|
|
@ -68,6 +68,7 @@ module Arel
|
||||||
end
|
end
|
||||||
|
|
||||||
class TableReference < Formatter
|
class TableReference < Formatter
|
||||||
|
attr_reader :christener
|
||||||
delegate :name_for, :to => :@christener
|
delegate :name_for, :to => :@christener
|
||||||
|
|
||||||
def initialize(christener)
|
def initialize(christener)
|
||||||
|
|
|
@ -155,7 +155,7 @@ module Arel
|
||||||
SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
|
SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
|
||||||
FROM `users`
|
FROM `users`
|
||||||
INNER JOIN `users` AS `users_2`
|
INNER JOIN `users` AS `users_2`
|
||||||
ON `users_2`.`id` = `users_3`.`id`
|
ON `users`.`id` = `users_2`.`id`
|
||||||
INNER JOIN `users` AS `users_3`
|
INNER JOIN `users` AS `users_3`
|
||||||
ON `users_2`.`id` = `users_3`.`id`
|
ON `users_2`.`id` = `users_3`.`id`
|
||||||
")
|
")
|
||||||
|
|
Loading…
Reference in a new issue