1
0
Fork 0
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:
Nick Kallen 2008-05-01 23:19:12 -07:00
parent 6c61ebb4d6
commit 85bc3b417d
6 changed files with 29 additions and 37 deletions

View file

@ -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" }

View file

@ -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

View file

@ -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

View file

@ -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? ),

View file

@ -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)

View file

@ -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`
") ")