aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/arel/primitives/attribute.rb13
-rw-r--r--lib/arel/primitives/value.rb4
-rw-r--r--lib/arel/relations/alias.rb12
-rw-r--r--lib/arel/relations/compound.rb4
-rw-r--r--lib/arel/relations/grouping.rb8
-rw-r--r--lib/arel/relations/join.rb42
-rw-r--r--lib/arel/relations/nil.rb1
-rw-r--r--lib/arel/relations/relation.rb37
-rw-r--r--lib/arel/relations/table.rb12
-rw-r--r--lib/arel/sql.rb49
-rw-r--r--spec/arel/unit/relations/grouping_spec.rb4
-rw-r--r--spec/arel/unit/relations/join_spec.rb22
-rw-r--r--spec/arel/unit/relations/projection_spec.rb11
-rw-r--r--spec/arel/unit/relations/table_spec.rb7
14 files changed, 120 insertions, 106 deletions
diff --git a/lib/arel/primitives/attribute.rb b/lib/arel/primitives/attribute.rb
index 797ebfd07b..9215ea64a0 100644
--- a/lib/arel/primitives/attribute.rb
+++ b/lib/arel/primitives/attribute.rb
@@ -7,6 +7,7 @@ module Arel
@relation, @name, @alias, @ancestor = relation, name, options[:alias], options[:ancestor]
end
+ # INVESTIGATE
def alias_or_name
@alias || name
end
@@ -117,14 +118,22 @@ module Arel
end
include Expressions
- def to_sql(formatter = Sql::WhereCondition.new(engine))
- formatter.attribute relation.prefix_for(self), name, self.alias
+ def to_sql(formatter = Sql::WhereCondition.new(relation))
+ formatter.attribute self
end
def format(object)
object.to_sql(formatter)
end
+ def original_relation
+ relation.relation_for(self)
+ end
+
+ def christener
+ relation.christener
+ end
+
private
def formatter
Sql::Attribute.new(self)
diff --git a/lib/arel/primitives/value.rb b/lib/arel/primitives/value.rb
index 650557559a..5142eb45ca 100644
--- a/lib/arel/primitives/value.rb
+++ b/lib/arel/primitives/value.rb
@@ -8,12 +8,12 @@ module Arel
@value, @relation = value, relation
end
- def to_sql(formatter = Sql::WhereCondition.new(relation.engine))
+ def to_sql(formatter = Sql::WhereCondition.new(relation))
formatter.value value
end
def format(object)
- object.to_sql(Sql::Value.new(relation.engine))
+ object.to_sql(Sql::Value.new(relation))
end
def ==(other)
diff --git a/lib/arel/relations/alias.rb b/lib/arel/relations/alias.rb
index 329f94638e..b4e8965625 100644
--- a/lib/arel/relations/alias.rb
+++ b/lib/arel/relations/alias.rb
@@ -1,13 +1,19 @@
module Arel
class Alias < Compound
- attr_reader :alias
-
def initialize(relation)
@relation = relation
end
def ==(other)
- self.equal? other
+ equal? other
+ end
+
+ def table
+ self
+ end
+
+ def relation_for(attribute)
+ self[attribute] and self
end
end
end \ No newline at end of file
diff --git a/lib/arel/relations/compound.rb b/lib/arel/relations/compound.rb
index 735f586114..7367e60a2d 100644
--- a/lib/arel/relations/compound.rb
+++ b/lib/arel/relations/compound.rb
@@ -5,8 +5,8 @@ module Arel
hash_on :relation
delegate :joins, :selects, :orders, :groupings, :inserts, :taken,
- :skipped, :name, :alias, :aggregation?, :prefix_for, :column_for,
- :engine, :name_for,
+ :skipped, :name, :alias, :aggregation?, :column_for,
+ :engine, :name_for, :table, :relation_for,
:to => :relation
def attributes
diff --git a/lib/arel/relations/grouping.rb b/lib/arel/relations/grouping.rb
index 2815f62b79..e3686f7f28 100644
--- a/lib/arel/relations/grouping.rb
+++ b/lib/arel/relations/grouping.rb
@@ -15,5 +15,13 @@ module Arel
def aggregation?
true
end
+
+ def table_sql(formatter = Sql::TableReference.new(self))
+ to_sql(formatter)
+ end
+
+ def name
+ table.name + '_aggregation'
+ end
end
end \ No newline at end of file
diff --git a/lib/arel/relations/join.rb b/lib/arel/relations/join.rb
index cb24ab7717..20c19e0848 100644
--- a/lib/arel/relations/join.rb
+++ b/lib/arel/relations/join.rb
@@ -23,10 +23,6 @@ module Arel
externalize(relation2).attributes).collect { |a| a.bind(self) }
end
- def prefix_for(attribute)
- externalize(relation_for(attribute)).prefix_for(attribute)
- end
-
# TESTME: Not sure which scenario needs this method, was driven by failing tests in ActiveRecord
def column_for(attribute)
(relation1[attribute] || relation2[attribute]).column
@@ -35,7 +31,11 @@ module Arel
def joins(formatter = Sql::TableReference.new(self))
this_join = [
join_sql,
- externalize(relation2).table_sql(formatter),
+ if relation2.aggregation?
+ relation2.to_sql(formatter)
+ else
+ relation2.table.table_sql(formatter)
+ end,
("ON" unless predicates.blank?),
predicates.collect { |p| p.bind(formatter.christener).to_sql }.join(' AND ')
].compact.join(" ")
@@ -46,10 +46,6 @@ module Arel
(externalize(relation1).selects + externalize(relation2).selects).collect { |s| s.bind(self) }
end
- def table_sql(formatter = Sql::TableReference.new(self))
- externalize(relation1).table_sql(formatter)
- end
-
def name_for(relation)
@used_names ||= Hash.new(0)
@relation_names ||= Hash.new do |h, k|
@@ -59,11 +55,21 @@ module Arel
@relation_names[relation]
end
- protected
+ def table
+ relation1.aggregation?? relation1 : relation1.table
+ end
+
+ delegate :name, :to => :relation1
+
def relation_for(attribute)
- [relation1[attribute], relation2[attribute]].select { |a| a =~ attribute }.min do |a1, a2|
+ x = [relation1[attribute], relation2[attribute]].select { |a| a =~ attribute }.min do |a1, a2|
(attribute % a1).size <=> (attribute % a2).size
- end.relation.relation_for(attribute)
+ end.relation
+ if x.aggregation?
+ x
+ else
+ x.relation_for(attribute) # FIXME @demeter
+ end
end
private
@@ -74,14 +80,6 @@ module Arel
Externalizer = Struct.new(:christener, :relation) do
delegate :engine, :to => :relation
- def table_sql(formatter = Sql::TableReference.new(self))
- if relation.aggregation?
- relation.to_sql(formatter) + ' AS ' + engine.quote_table_name(formatter.name_for(relation) + (relation.aggregation?? '_aggregation' : ''))
- else
- relation.table_sql(formatter)
- end
- end
-
def selects
relation.aggregation?? [] : relation.selects
end
@@ -89,10 +87,6 @@ module Arel
def attributes
relation.aggregation?? relation.attributes.collect(&:to_attribute) : relation.attributes
end
-
- def prefix_for(attribute)
- christener.name_for(relation) + (relation.aggregation?? '_aggregation' : '')
- end
end
end
end \ No newline at end of file
diff --git a/lib/arel/relations/nil.rb b/lib/arel/relations/nil.rb
index 67eb2d3a62..258f60f478 100644
--- a/lib/arel/relations/nil.rb
+++ b/lib/arel/relations/nil.rb
@@ -1,5 +1,6 @@
module Arel
class Nil < Relation
+ def table; self end
def table_sql(formatter = nil); '' end
def name; '' end
def to_s; '' end
diff --git a/lib/arel/relations/relation.rb b/lib/arel/relations/relation.rb
index 7b414b9768..e08123eaf5 100644
--- a/lib/arel/relations/relation.rb
+++ b/lib/arel/relations/relation.rb
@@ -99,36 +99,40 @@ module Arel
def aggregation?
false
end
-
- def relation_for(attribute)
- self[attribute] and self
- end
def name_for(relation)
relation.name
end
def table_sql(formatter = Sql::TableReference.new(self))
- formatter.table name, formatter.name_for(self)
+ formatter.table self
end
end
include Externalizable
- def to_sql(formatter = Sql::SelectStatement.new(engine))
- tr = Sql::TableReference.new(self)
+ def to_sql(formatter = Sql::SelectStatement.new(self))
formatter.select [
- "SELECT #{attributes.collect { |a| a.to_sql(Sql::SelectClause.new(engine)) }.join(', ')}",
- "FROM #{table_sql(tr)}",
- (joins(tr) unless joins.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? ),
+ "SELECT #{attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }.join(', ')}",
+ "FROM #{thing}",
+ (joins(Sql::TableReference.new(self)) unless joins.blank? ),
+ ("WHERE #{selects.collect { |s| s.to_sql(Sql::WhereClause.new(self)) }.join("\n\tAND ")}" unless selects.blank? ),
+ ("ORDER BY #{orders.collect { |o| o.to_sql(Sql::OrderClause.new(self)) }.join(', ')}" unless orders.blank? ),
("GROUP BY #{groupings.collect(&:to_sql)}" unless groupings.blank? ),
("LIMIT #{taken}" unless taken.blank? ),
("OFFSET #{skipped}" unless skipped.blank? )
- ].compact.join("\n")
+ ].compact.join("\n"), name
end
alias_method :to_s, :to_sql
+ # FIXME
+ def thing
+ if table.aggregation?
+ table.to_sql(Sql::TableReference.new(self))
+ else
+ table.table_sql(Sql::TableReference.new(self))
+ end
+ end
+
def inclusion_predicate_sql
"IN"
end
@@ -159,8 +163,13 @@ module Arel
self
end
+ # INVESTIGATE
def format(object)
- object.to_sql(Sql::WhereCondition.new(engine))
+ object.to_sql(Sql::WhereCondition.new(self))
+ end
+
+ def christener
+ self
end
def attributes; [] end
diff --git a/lib/arel/relations/table.rb b/lib/arel/relations/table.rb
index 9c7d90d4fc..fcde94b595 100644
--- a/lib/arel/relations/table.rb
+++ b/lib/arel/relations/table.rb
@@ -15,14 +15,18 @@ module Arel
end
end
- def prefix_for(attribute)
- self[attribute] and name
- end
-
def column_for(attribute)
self[attribute] and columns.detect { |c| c.name == attribute.name.to_s }
end
+ def table
+ self
+ end
+
+ def relation_for(attribute)
+ self[attribute] and self
+ end
+
def ==(other)
self.class == other.class and
name == other.name
diff --git a/lib/arel/sql.rb b/lib/arel/sql.rb
index 7a620009db..de0bece372 100644
--- a/lib/arel/sql.rb
+++ b/lib/arel/sql.rb
@@ -5,22 +5,23 @@ module Arel
end
class Formatter
- attr_reader :engine
+ attr_reader :engine, :christener
include Quoting
- def initialize(engine)
- @engine = engine
+ def initialize(environment)
+ @christener, @engine = environment.christener, environment.engine
end
end
class SelectClause < Formatter
- def attribute(relation_name, attribute_name, aliaz)
- "#{quote_table_name(relation_name)}.#{quote_column_name(attribute_name)}" + (aliaz ? " AS #{quote(aliaz.to_s)}" : "")
+ def attribute(attribute)
+ relation_name = @christener.name_for(attribute.original_relation)
+ "#{quote_table_name(relation_name)}.#{quote_column_name(attribute.name)}" + (attribute.alias ? " AS #{quote(attribute.alias.to_s)}" : "")
end
- def select(select_sql)
- "(#{select_sql})"
+ def select(select_sql, name)
+ "(#{select_sql}) AS #{quote_table_name(name.to_s)}"
end
def value(value)
@@ -37,15 +38,17 @@ module Arel
class WhereClause < PassThrough
end
- class OrderClause < PassThrough
- def attribute(relation_name, attribute_name, aliaz)
- "#{quote_table_name(relation_name)}.#{quote_column_name(attribute_name)}"
+ class OrderClause < PassThrough
+ def attribute(attribute)
+ relation_name = @christener.name_for(attribute.original_relation)
+ "#{quote_table_name(relation_name)}.#{quote_column_name(attribute.name)}"
end
end
class WhereCondition < Formatter
- def attribute(relation_name, attribute_name, aliaz)
- "#{quote_table_name(relation_name)}.#{quote_column_name(attribute_name)}"
+ def attribute(attribute)
+ relation_name = @christener.name_for(attribute.original_relation)
+ "#{quote_table_name(relation_name)}.#{quote_column_name(attribute.name)}"
end
def value(value)
@@ -56,37 +59,31 @@ module Arel
quote(value, column)
end
- def select(select_sql)
+ def select(select_sql, name)
"(#{select_sql})"
end
end
class SelectStatement < Formatter
- def select(select_sql)
+ def select(select_sql, name)
select_sql
end
end
class TableReference < Formatter
- attr_reader :christener
- delegate :name_for, :to => :@christener
-
- def initialize(christener)
- @christener, @engine = christener, christener.engine
- end
-
- def select(select_sql)
- "(#{select_sql})"
+ def select(select_sql, name)
+ "(#{select_sql}) AS #{quote_table_name(name)}"
end
- def table(name, aliaz)
- quote_table_name(name) + (name != aliaz ? " AS " + engine.quote_table_name(aliaz) : '')
+ def table(table)
+ aliaz = christener.name_for(table)
+ quote_table_name(table.name) + (table.name != aliaz ? " AS " + engine.quote_table_name(aliaz) : '')
end
end
class Attribute < WhereCondition
def initialize(attribute)
- @attribute, @engine = attribute, attribute.engine
+ @attribute, @christener, @engine = attribute, attribute.christener, attribute.engine
end
def scalar(scalar)
diff --git a/spec/arel/unit/relations/grouping_spec.rb b/spec/arel/unit/relations/grouping_spec.rb
index bef3566896..66205b8f95 100644
--- a/spec/arel/unit/relations/grouping_spec.rb
+++ b/spec/arel/unit/relations/grouping_spec.rb
@@ -6,7 +6,7 @@ module Arel
@relation = Table.new(:users)
@attribute = @relation[:id]
end
-
+
describe '#to_sql' do
describe 'when given a predicate' do
it "manufactures sql with where clause conditions" do
@@ -20,7 +20,7 @@ module Arel
describe 'when given a string' do
it "passes the string through to the where clause" do
- pending 'it should not quote asdf'
+ pending 'it should not quote the group clause'
Grouping.new(@relation, 'asdf').to_sql.should be_like("
SELECT `users`.`id`, `users`.`name`
FROM `users`
diff --git a/spec/arel/unit/relations/join_spec.rb b/spec/arel/unit/relations/join_spec.rb
index 0c2d1d771d..52e1a93b8e 100644
--- a/spec/arel/unit/relations/join_spec.rb
+++ b/spec/arel/unit/relations/join_spec.rb
@@ -47,15 +47,6 @@ module Arel
end
end
- describe '#prefix_for' do
- it "returns the name of the relation containing the attribute" do
- Join.new("INNER JOIN", @relation1, @relation2, @predicate).prefix_for(@relation1[:id]) \
- .should == @relation1.prefix_for(@relation1[:id])
- Join.new("INNER JOIN", @relation1, @relation2, @predicate).prefix_for(@relation2[:id]) \
- .should == @relation2.prefix_for(@relation2[:id])
- end
- end
-
describe '#to_sql' do
it 'manufactures sql joining the two tables on the predicate' do
Join.new("INNER JOIN", @relation1, @relation2, @predicate).to_sql.should be_like("
@@ -116,6 +107,7 @@ module Arel
end
end
+ # TESTME try other direction too!
it "keeps selects on the aggregation within the derived table" do
Join.new("INNER JOIN", @relation1, @aggregation.select(@aggregation[:user_id].eq(1)), @predicate).to_sql.should be_like("
SELECT `users`.`id`, `users`.`name`, `photos_aggregation`.`user_id`, `photos_aggregation`.`cnt`
@@ -133,16 +125,6 @@ module Arel
@predicate = @relation1[:id].eq(@aliased_relation[:id])
end
- describe '#prefix_for' do
- it "returns the alias of the relation containing the attribute" do
- relation = Join.new("INNER JOIN", @relation1, @aliased_relation, @predicate)
- relation.prefix_for(@aliased_relation[:id]) \
- .should == @relation1.name
- relation.prefix_for(@relation1[:id]) \
- .should == @relation1.name + '_2'
- end
- end
-
describe 'when joining the same relation to itself' do
describe '#to_sql' do
it '' do
@@ -151,6 +133,7 @@ module Arel
@relation1 \
.join(relation2.join(relation3).on(relation2[:id].eq(relation3[:id]))) \
.on(@relation1[:id].eq(relation2[:id])) \
+ .select(@relation1[:id].eq(1)) \
.to_sql.should be_like("
SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
FROM `users`
@@ -158,6 +141,7 @@ module Arel
ON `users`.`id` = `users_2`.`id`
INNER JOIN `users` AS `users_3`
ON `users_2`.`id` = `users_3`.`id`
+ WHERE `users`.`id` = 1
")
end
diff --git a/spec/arel/unit/relations/projection_spec.rb b/spec/arel/unit/relations/projection_spec.rb
index cd59d6c383..eedcf77952 100644
--- a/spec/arel/unit/relations/projection_spec.rb
+++ b/spec/arel/unit/relations/projection_spec.rb
@@ -47,7 +47,7 @@ module Arel
it "manufactures sql with scalar selects" do
Projection.new(@relation, @scalar_relation).to_sql.should be_like("
- SELECT (SELECT `users`.`name` FROM `users`) FROM `users`
+ SELECT (SELECT `users`.`name` FROM `users`) AS `users` FROM `users`
")
end
end
@@ -59,6 +59,15 @@ module Arel
")
end
end
+
+ describe 'when given an expression' do
+ it 'manufactures sql with expressions' do
+ @relation.project(@attribute.count).to_sql.should be_like("
+ SELECT COUNT(`users`.`id`)
+ FROM `users`
+ ")
+ end
+ end
end
describe Projection::Externalizable do
diff --git a/spec/arel/unit/relations/table_spec.rb b/spec/arel/unit/relations/table_spec.rb
index eb765ce777..54520bf3b6 100644
--- a/spec/arel/unit/relations/table_spec.rb
+++ b/spec/arel/unit/relations/table_spec.rb
@@ -51,13 +51,6 @@ module Arel
end
end
- describe '#prefix_for' do
- it "returns the table name if the relation contains the attribute" do
- @relation.prefix_for(@relation[:id]).should == 'users'
- @relation.prefix_for(:does_not_exist).should be_nil
- end
- end
-
describe '#attributes' do
it 'manufactures attributes corresponding to columns in the table' do
@relation.attributes.should == [