mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
renamed select operation to where
This commit is contained in:
parent
14210279b2
commit
3eae3b08ee
21 changed files with 168 additions and 108 deletions
123
README
123
README
|
@ -1,8 +1,16 @@
|
|||
== Abstract ==
|
||||
## Abstract ##
|
||||
|
||||
Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) transparently adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.
|
||||
Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.
|
||||
|
||||
== A Gentle Introduction ==
|
||||
## Status ##
|
||||
|
||||
Arel is alpha software, BEWARE. Nevertheless, at this point, many (most?) SELECT queries can be composed, including very very complicated ones. Writes are only experimental for now.
|
||||
|
||||
For the moment, Arel uses ActiveRecord's connection adapters to connect to the various engines, connection pooling, perform quoting, and do type conversion. On the horizon is the use of DataObjects instead.
|
||||
|
||||
The long term goal, following both LINQ and DataMapper, is to have Arel adapt to engines beyond RDBMS, including XML, IMAP, YAML, etc.
|
||||
|
||||
## A Gentle Introduction ##
|
||||
|
||||
Generating a query with ARel is simple. For example, in order to produce
|
||||
|
||||
|
@ -10,56 +18,107 @@ Generating a query with ARel is simple. For example, in order to produce
|
|||
|
||||
you construct a table relation and convert it to sql:
|
||||
|
||||
Arel::Table.new(:users).to_sql
|
||||
users = Arel::Table.new(:users)
|
||||
users.to_sql
|
||||
|
||||
In fact, you will probably never call `#to_sql`. Let `users = Arel::Table.new(:users)`. Rather, you'll work with data from the table directly. You can iterate through all rows in the `users` table like this:
|
||||
In fact, you will probably never call `#to_sql`. Rather, you'll work with data from the table directly. You can iterate through all rows in the `users` table like this:
|
||||
|
||||
users.each { |user| ... }
|
||||
|
||||
In other words, Arel relations behave implement Ruby's Eunmerable interface. Let's have a look at a concrete example:
|
||||
In other words, Arel relations implement Ruby's Enumerable interface. Let's have a look at a concrete example:
|
||||
|
||||
users.first # => {'id' => 10, 'name' => 'bob'}
|
||||
users.first # => { users[:id] => 1, users[:name] => 'bob' }
|
||||
|
||||
As you can see, Arel converts the rows from the database into a hash, the values of which are sublimated to the appropriate Ruby primitive (integers, strings, and so forth).
|
||||
|
||||
== Relational Algebra ==
|
||||
### More Sophisticated <strike>Queries</strike> Relations ###
|
||||
|
||||
Arel is based on the Relational Algebra, a mathematical model that is also the inspiration for relational databases. Arel::Relation objects do not represent queries per se (i.e., they are not object-representations of `SELECT`, `INSERT`, `UPDATE`, or `DELETE` statements), rather they represent a collection of data that you can select from, insert into, update, and delete. For example, to insert a row into the users table, do the following:
|
||||
Here is a whirlwind tour through the most common relational operators. These will probably cover 80% of all interaction with the database.
|
||||
|
||||
users.insert({users[:name] => 'amy'}) # => INSERT INTO users (users.name) VALUES ('amy')
|
||||
|
||||
To delete all users:
|
||||
First is the 'restriction' operator, `where`:
|
||||
|
||||
users.delete # => DELETE FROM users
|
||||
|
||||
To update:
|
||||
users.where(users[:name].eq('amy'))
|
||||
# => SELECT * FROM users WHERE users.name = 'amy'
|
||||
|
||||
users.update({users[:name] => 'carl'}) # => UPDATE users SET name = 'carl'
|
||||
|
||||
As you can see, the `relation` named `users` does not represent an individual query; rather it is an abstraction on a collection of data and it can produce appropriate SQL queries to do the various CRUD operations.
|
||||
|
||||
=== More Sophisticated <strike>Queries</strike> Relations ===
|
||||
|
||||
Following the Relational Algebra, Arel's interface uses some jargon that differs from standard SQL. For example, in order to add a `WHERE` clause to your relations, you use the `select` operation:
|
||||
|
||||
users.select(users[:name].eq('amy')) # => SELECT * FROM users WHERE users.name = 'amy'
|
||||
|
||||
What would, in SQL, be part of the `SELECT` clause is called here a `projection`:
|
||||
What would, in SQL, be part of the `SELECT` clause is called in Arel a `projection`:
|
||||
|
||||
users.project(users[:id]) # => SELECT users.id FROM users
|
||||
|
||||
Joins are fairly straightforward:
|
||||
Joins resemble SQL strongly:
|
||||
|
||||
users.join(photos).on(users[:id].eq(photos[:user_id])) => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id
|
||||
users.join(photos).on(users[:id].eq(photos[:user_id]))
|
||||
# => SELECT * FROM users INNER JOIN photos ON users.id = photos.user_id
|
||||
|
||||
The best property of the Relational is compositionality, or closure under all operations. For example, to select and project:
|
||||
What are called `LIMIT` and `OFFSET` in SQL are called `take` and `skip` in Arel:
|
||||
|
||||
users.take(5) # => SELECT * FROM users LIMIT 5
|
||||
users.skip(4) # => SELECT * FROM users OFFSET 4
|
||||
|
||||
`GROUP BY` is called `group`:
|
||||
|
||||
users.group(users[:name]) # => SELECT * FROM users GROUP BY name
|
||||
|
||||
The best property of the Relational Algebra is its "composability", or closure under all operations. For example, to select AND project, just "chain" the method invocations:
|
||||
|
||||
users \
|
||||
.select(users[:name].eq('amy')) \
|
||||
.where(users[:name].eq('amy')) \
|
||||
.project(users[:id]) \
|
||||
# => SELECT users.id FROM users WHERE users.name = 'amy'
|
||||
|
||||
All operators are chainable in this way, and they are chainable any number of times, in any order.
|
||||
|
||||
== Contributions ==
|
||||
users.where(users[:name].eq('bob')).where(users[:age].lt(25))
|
||||
|
||||
Of course, many of the operators take multiple arguments, so the last example can be written more tersely:
|
||||
|
||||
users.where(users[:name].eq('bob'), users[:age].lt(25))
|
||||
|
||||
The `OR` operator is not yet supported. It will work like this:
|
||||
|
||||
users.where(users[:name].eq('bob').or(users[:age].lt(25)))
|
||||
|
||||
The `AND` operator will behave similarly.
|
||||
|
||||
### The Crazy Features ###
|
||||
|
||||
The examples above are fairly simple and other libraries match or come close to matching the expressiveness of Arel (e.g., `Sequel` in Ruby).
|
||||
|
||||
|
||||
#### Complex Joins ####
|
||||
|
||||
Where Arel really shines in its ability to handle complex joins and aggregations. As a first example, let's consider an "adjacency list", a tree represented in a table. Suppose we have a table `comments`, representing a threaded discussion:
|
||||
|
||||
comments = Arel::Table.new(:comments)
|
||||
|
||||
And this table has the following attributes:
|
||||
|
||||
comments.attributes # => [comments[:id], comments[:body], comments[:parent_id]]
|
||||
|
||||
The `parent_id` column is a foreign key from the `comments` table to itself. Now, joining a table to itself requires aliasing in SQL. In fact, you may alias in Arel as well:
|
||||
|
||||
replies = comments.alias
|
||||
comments_with_replies = \
|
||||
comments.join(replies).on(replies[:parent_id].eq(comments[:id]))
|
||||
# => SELECT * FROM comments INNER JOIN comments AS comments_2 WHERE comments_2.parent_id = comments.id
|
||||
|
||||
Arel will always produce a unique name for every table joined in the relation, and it will always do so deterministically to exploit query caching. Typically, the problem with automated table aliasing is that extracting data out of the result set when everything has a random name is quite hard. Arel makes this simple: to get just certain columns from the result set, treat a row like a hash:
|
||||
|
||||
comments_with_replies.first[replies[:body]]
|
||||
|
||||
This will return the first comment's reply's body.
|
||||
|
||||
Arel can actually perform the aliasing automatically, without the need for the programmer to explicitly call `alias`. However, this makes it difficult to specify the join condition:
|
||||
|
||||
comments.join(comments).on(comments[:parent_id].eq(comments[:id]))
|
||||
# => SELECT * FROM comments INNER JOIN comments AS comments_2 WHERE comments.parent_id = comments.id
|
||||
|
||||
This does NOT have the same meaning as the previous query. As an alternative to aliasing, there is a convenient block form:
|
||||
|
||||
comments.join(comments) { |comments, replies| replies[:parent_id].eq(comments[:id]) }
|
||||
|
||||
Of course, without the `alias`, you will have a harder time extracting `replies` data from a row.
|
||||
|
||||
#### Complex Aggregations ####
|
||||
|
||||
My personal favorite feature of Arel, and certainly the most difficult to implement, is closure under joining even in the presence of aggregations. This is a feature where the Relational Algebra is fundamentally easier to use than SQL.
|
||||
|
||||
I appreciate all contributions to Arel. There is only one "unusual" requirement I have concerning code style: all specs should be written without mocks, using concrete examples to elicit testable behavior. This has two benefits: it 1) ensures the tests serve as concrete documentation and 2) suits the functional nature of this library, which emphasizes algebraic transformation rather than decoupled components.
|
3
doc/TODO
3
doc/TODO
|
@ -1,7 +1,8 @@
|
|||
todo:
|
||||
- rename select to where
|
||||
- and/or w/ predicates
|
||||
- blocks for joins
|
||||
- blocks for all operations
|
||||
- result sets to attr correlation too
|
||||
- cache expiry on write
|
||||
- rewrite of arecord querycache test in light of this
|
||||
- scoped writes
|
||||
|
|
|
@ -8,7 +8,7 @@ require 'arel/relations/aggregation'
|
|||
require 'arel/relations/join'
|
||||
require 'arel/relations/grouping'
|
||||
require 'arel/relations/projection'
|
||||
require 'arel/relations/selection'
|
||||
require 'arel/relations/where'
|
||||
require 'arel/relations/order'
|
||||
require 'arel/relations/take'
|
||||
require 'arel/relations/skip'
|
||||
|
|
|
@ -6,7 +6,7 @@ module Arel
|
|||
@relation = relation
|
||||
end
|
||||
|
||||
def selects
|
||||
def wheres
|
||||
[]
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module Arel
|
|||
class Compound < Relation
|
||||
attr_reader :relation
|
||||
hash_on :relation
|
||||
delegate :joins, :selects, :join?, :orders, :groupings, :inserts, :taken,
|
||||
delegate :joins, :wheres, :join?, :inserts, :taken,
|
||||
:skipped, :name, :aggregation?, :column_for,
|
||||
:engine, :table, :table_sql,
|
||||
:to => :relation
|
||||
|
@ -11,8 +11,8 @@ module Arel
|
|||
@attributes ||= relation.attributes.collect { |a| a.bind(self) }
|
||||
end
|
||||
|
||||
def selects
|
||||
@selects ||= relation.selects.collect { |s| s.bind(self) }
|
||||
def wheres
|
||||
@wheres ||= relation.wheres.collect { |w| w.bind(self) }
|
||||
end
|
||||
|
||||
def groupings
|
||||
|
|
|
@ -8,7 +8,7 @@ module Arel
|
|||
[
|
||||
"DELETE",
|
||||
"FROM #{table_sql}",
|
||||
("WHERE #{selects.collect(&:to_sql).join('\n\tAND ')}" unless selects.blank? ),
|
||||
("WHERE #{wheres.collect(&:to_sql).join('\n\tAND ')}" unless wheres.blank? ),
|
||||
("LIMIT #{taken}" unless taken.blank? ),
|
||||
].compact.join("\n")
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module Arel
|
|||
join_sql,
|
||||
relation2.externalize.table_sql(formatter),
|
||||
("ON" unless predicates.blank?),
|
||||
(ons + relation2.externalize.selects).collect { |p| p.bind(environment).to_sql(Sql::WhereClause.new(environment)) }.join(' AND ')
|
||||
(ons + relation2.externalize.wheres).collect { |p| p.bind(environment).to_sql(Sql::WhereClause.new(environment)) }.join(' AND ')
|
||||
].compact.join(" ")
|
||||
[relation1.joins(environment), this_join, relation2.joins(environment)].compact.join(" ")
|
||||
end
|
||||
|
@ -29,8 +29,8 @@ module Arel
|
|||
relation2.externalize.attributes).collect { |a| a.bind(self) }
|
||||
end
|
||||
|
||||
def selects
|
||||
relation1.externalize.selects
|
||||
def wheres
|
||||
relation1.externalize.wheres
|
||||
end
|
||||
|
||||
def ons
|
||||
|
|
|
@ -14,7 +14,7 @@ module Arel
|
|||
"SELECT #{attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }.join(', ')}",
|
||||
"FROM #{table_sql(Sql::TableReference.new(self))}",
|
||||
(joins(self) unless joins(self).blank? ),
|
||||
("WHERE #{selects .collect { |s| s.to_sql(Sql::WhereClause.new(self)) }.join("\n\tAND ")}" unless selects.blank? ),
|
||||
("WHERE #{wheres .collect { |w| w.to_sql(Sql::WhereClause.new(self)) }.join("\n\tAND ")}" unless wheres.blank? ),
|
||||
("ORDER BY #{orders .collect { |o| o.to_sql(Sql::OrderClause.new(self)) }.join(', ')}" unless orders.blank? ),
|
||||
("GROUP BY #{groupings.collect { |g| g.to_sql(Sql::GroupClause.new(self)) }.join(', ')}" unless groupings.blank? ),
|
||||
("LIMIT #{taken}" unless taken.blank? ),
|
||||
|
@ -72,8 +72,8 @@ module Arel
|
|||
join(other, "LEFT OUTER JOIN")
|
||||
end
|
||||
|
||||
def select(*predicates)
|
||||
predicates.all?(&:blank?) ? self : Selection.new(self, *predicates)
|
||||
def where(*predicates)
|
||||
predicates.all?(&:blank?) ? self : Where.new(self, *predicates)
|
||||
end
|
||||
|
||||
def project(*attributes)
|
||||
|
@ -161,7 +161,7 @@ module Arel
|
|||
|
||||
module DefaultOperations
|
||||
def attributes; [] end
|
||||
def selects; [] end
|
||||
def wheres; [] end
|
||||
def orders; [] end
|
||||
def inserts; [] end
|
||||
def groupings; [] end
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
module Arel
|
||||
class Selection < Compound
|
||||
attr_reader :predicate
|
||||
|
||||
def initialize(relation, *predicates)
|
||||
predicate = predicates.shift
|
||||
@relation = predicates.empty?? relation : Selection.new(relation, *predicates)
|
||||
@predicate = predicate.bind(@relation)
|
||||
end
|
||||
|
||||
def selects
|
||||
@selects ||= (relation.selects + [predicate]).collect { |p| p.bind(self) }
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
Selection === other and
|
||||
relation == other.relation and
|
||||
predicate == other.predicate
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,7 +12,7 @@ module Arel
|
|||
assignments.collect do |attribute, value|
|
||||
"#{value.format(attribute)} = #{attribute.format(value)}"
|
||||
end.join(",\n"),
|
||||
("WHERE #{selects.collect(&:to_sql).join('\n\tAND ')}" unless selects.blank? ),
|
||||
("WHERE #{wheres.collect(&:to_sql).join('\n\tAND ')}" unless wheres.blank? ),
|
||||
("LIMIT #{taken}" unless taken.blank? )
|
||||
].join("\n")
|
||||
end
|
||||
|
|
21
lib/arel/relations/where.rb
Normal file
21
lib/arel/relations/where.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
module Arel
|
||||
class Where < Compound
|
||||
attr_reader :predicate
|
||||
|
||||
def initialize(relation, *predicates)
|
||||
predicate = predicates.shift
|
||||
@relation = predicates.empty?? relation : Where.new(relation, *predicates)
|
||||
@predicate = predicate.bind(@relation)
|
||||
end
|
||||
|
||||
def wheres
|
||||
@wheres ||= (relation.wheres + [predicate]).collect { |p| p.bind(self) }
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
Where === other and
|
||||
relation == other.relation and
|
||||
predicate == other.predicate
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,10 +19,10 @@ module Arel
|
|||
")
|
||||
end
|
||||
|
||||
describe 'when joining with a selection on the same relation' do
|
||||
describe 'when joining with a where on the same relation' do
|
||||
it 'manufactures sql aliasing the tables properly' do
|
||||
@relation1 \
|
||||
.join(@relation2.select(@relation2[:id].eq(1))) \
|
||||
.join(@relation2.where(@relation2[:id].eq(1))) \
|
||||
.on(@predicate) \
|
||||
.to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
|
||||
|
@ -32,9 +32,9 @@ module Arel
|
|||
")
|
||||
end
|
||||
|
||||
describe 'when the selection occurs before the alias' do
|
||||
describe 'when the where occurs before the alias' do
|
||||
it 'manufactures sql aliasing the predicates properly' do
|
||||
relation2 = @relation1.select(@relation1[:id].eq(1)).alias
|
||||
relation2 = @relation1.where(@relation1[:id].eq(1)).alias
|
||||
@relation1 \
|
||||
.join(relation2) \
|
||||
.on(relation2[:id].eq(@relation1[:id])) \
|
||||
|
@ -100,7 +100,7 @@ module Arel
|
|||
|
||||
describe 'when both relations are compound and only one is an alias' do
|
||||
it 'disambiguates the relation that serves as the ancestor to the attribute' do
|
||||
compound1 = @relation1.select(@predicate)
|
||||
compound1 = @relation1.where(@predicate)
|
||||
compound2 = compound1.alias
|
||||
compound1 \
|
||||
.join(compound2) \
|
||||
|
@ -112,8 +112,8 @@ module Arel
|
|||
describe 'when the left relation is extremely compound' do
|
||||
it 'disambiguates the relation that serves as the ancestor to the attribute' do
|
||||
@relation1 \
|
||||
.select(@predicate) \
|
||||
.select(@predicate) \
|
||||
.where(@predicate) \
|
||||
.where(@predicate) \
|
||||
.join(@relation2) \
|
||||
.on(@predicate) \
|
||||
.should disambiguate_attributes(@relation1[:id], @relation2[:id])
|
||||
|
@ -125,9 +125,9 @@ module Arel
|
|||
@relation1 \
|
||||
.join( \
|
||||
@relation2 \
|
||||
.select(@predicate) \
|
||||
.select(@predicate) \
|
||||
.select(@predicate)) \
|
||||
.where(@predicate) \
|
||||
.where(@predicate) \
|
||||
.where(@predicate)) \
|
||||
.on(@predicate) \
|
||||
.should disambiguate_attributes(@relation1[:id], @relation2[:id])
|
||||
end
|
||||
|
|
|
@ -50,10 +50,10 @@ module Arel
|
|||
end
|
||||
end
|
||||
|
||||
describe 'when the aggration has a selection' do
|
||||
describe 'when the aggration has a where' do
|
||||
describe 'with the aggregation on the left' do
|
||||
it "manufactures sql keeping selects on the aggregation within the derived table" do
|
||||
@relation1.join(@aggregation.select(@aggregation[:user_id].eq(1))).on(@predicate).to_sql.should be_like("
|
||||
it "manufactures sql keeping wheres on the aggregation within the derived table" do
|
||||
@relation1.join(@aggregation.where(@aggregation[:user_id].eq(1))).on(@predicate).to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`, `photos_aggregation`.`user_id`, `photos_aggregation`.`cnt`
|
||||
FROM `users`
|
||||
INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_aggregation`
|
||||
|
@ -63,8 +63,8 @@ module Arel
|
|||
end
|
||||
|
||||
describe 'with the aggregation on the right' do
|
||||
it "manufactures sql keeping selects on the aggregation within the derived table" do
|
||||
@aggregation.select(@aggregation[:user_id].eq(1)).join(@relation1).on(@predicate).to_sql.should be_like("
|
||||
it "manufactures sql keeping wheres on the aggregation within the derived table" do
|
||||
@aggregation.where(@aggregation[:user_id].eq(1)).join(@relation1).on(@predicate).to_sql.should be_like("
|
||||
SELECT `photos_aggregation`.`user_id`, `photos_aggregation`.`cnt`, `users`.`id`, `users`.`name`
|
||||
FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_aggregation`
|
||||
INNER JOIN `users`
|
||||
|
|
|
@ -9,11 +9,11 @@ module Arel
|
|||
end
|
||||
|
||||
describe '#to_sql' do
|
||||
describe 'when the join contains a select' do
|
||||
describe 'and the select is given a string' do
|
||||
describe 'when the join contains a where' do
|
||||
describe 'and the where is given a string' do
|
||||
it 'does not escape the string' do
|
||||
@relation1 \
|
||||
.join(@relation2.select("asdf")) \
|
||||
.join(@relation2.where("asdf")) \
|
||||
.on(@predicate) \
|
||||
.to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
|
||||
|
@ -26,13 +26,13 @@ module Arel
|
|||
end
|
||||
|
||||
describe 'when a compound contains a join' do
|
||||
describe 'and the compound is a select' do
|
||||
describe 'and the compound is a where' do
|
||||
it 'manufactures sql disambiguating the tables' do
|
||||
@relation1 \
|
||||
.select(@relation1[:id].eq(1)) \
|
||||
.where(@relation1[:id].eq(1)) \
|
||||
.join(@relation2) \
|
||||
.on(@predicate) \
|
||||
.select(@relation1[:id].eq(1)) \
|
||||
.where(@relation1[:id].eq(1)) \
|
||||
.to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
|
||||
FROM `users`
|
||||
|
|
|
@ -16,7 +16,7 @@ module Arel
|
|||
|
||||
describe '#bind' do
|
||||
it "manufactures an attribute with the relation bound and self as an ancestor" do
|
||||
derived_relation = @relation.select(@relation[:id].eq(1))
|
||||
derived_relation = @relation.where(@relation[:id].eq(1))
|
||||
@attribute.bind(derived_relation).should == Attribute.new(derived_relation, @attribute.name, :ancestor => @attribute)
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ module Arel
|
|||
|
||||
describe '#bind' do
|
||||
it "manufactures an attribute with a rebound relation and self as the ancestor" do
|
||||
derived_relation = @relation.select(@relation[:id].eq(1))
|
||||
derived_relation = @relation.where(@relation[:id].eq(1))
|
||||
@expression.bind(derived_relation).should == Expression.new(@attribute.bind(derived_relation), "COUNT", nil, @expression)
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module Arel
|
|||
describe 'when there is no ambiguity' do
|
||||
it 'does not alias table names anywhere a table name can appear' do
|
||||
@relation \
|
||||
.select(@relation[:id].eq(1)) \
|
||||
.where(@relation[:id].eq(1)) \
|
||||
.order(@relation[:id]) \
|
||||
.project(@relation[:id]) \
|
||||
.group(@relation[:id]) \
|
||||
|
|
|
@ -14,8 +14,8 @@ module Arel
|
|||
")
|
||||
end
|
||||
|
||||
it 'manufactures sql deleting a selection relation' do
|
||||
Deletion.new(@relation.select(@relation[:id].eq(1))).to_sql.should be_like("
|
||||
it 'manufactures sql deleting a where relation' do
|
||||
Deletion.new(@relation.where(@relation[:id].eq(1))).to_sql.should be_like("
|
||||
DELETE
|
||||
FROM `users`
|
||||
WHERE `users`.`id` = 1
|
||||
|
|
|
@ -78,22 +78,22 @@ module Arel
|
|||
end
|
||||
end
|
||||
|
||||
describe '#select' do
|
||||
describe '#where' do
|
||||
before do
|
||||
@predicate = Equality.new(@attribute1, @attribute2)
|
||||
end
|
||||
|
||||
it "manufactures a selection relation" do
|
||||
@relation.select(@predicate).should == Selection.new(@relation, @predicate)
|
||||
it "manufactures a where relation" do
|
||||
@relation.where(@predicate).should == Where.new(@relation, @predicate)
|
||||
end
|
||||
|
||||
it "accepts arbitrary strings" do
|
||||
@relation.select("arbitrary").should == Selection.new(@relation, "arbitrary")
|
||||
@relation.where("arbitrary").should == Where.new(@relation, "arbitrary")
|
||||
end
|
||||
|
||||
describe 'when given a blank predicate' do
|
||||
it 'returns self' do
|
||||
@relation.select.should == @relation
|
||||
@relation.where.should == @relation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,15 +48,15 @@ module Arel
|
|||
end
|
||||
end
|
||||
|
||||
describe 'when the relation is a selection' do
|
||||
describe 'when the relation is a where' do
|
||||
before do
|
||||
@update = Update.new(
|
||||
@relation.select(@relation[:id].eq(1)),
|
||||
@relation.where(@relation[:id].eq(1)),
|
||||
@relation[:name] => "nick"
|
||||
)
|
||||
end
|
||||
|
||||
it 'manufactures sql updating a selection relation' do
|
||||
it 'manufactures sql updating a where relation' do
|
||||
@update.to_sql.should be_like("
|
||||
UPDATE `users`
|
||||
SET `users`.`name` = 'nick'
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
|
||||
|
||||
module Arel
|
||||
describe Selection do
|
||||
describe Where do
|
||||
before do
|
||||
@relation = Table.new(:users)
|
||||
@predicate = @relation[:id].eq(1)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it "manufactures nested selection relations if multiple predicates are provided" do
|
||||
it "manufactures nested where relations if multiple predicates are provided" do
|
||||
another_predicate = @relation[:name].lt(2)
|
||||
Selection.new(@relation, @predicate, another_predicate). \
|
||||
should == Selection.new(Selection.new(@relation, another_predicate), @predicate)
|
||||
Where.new(@relation, @predicate, another_predicate). \
|
||||
should == Where.new(Where.new(@relation, another_predicate), @predicate)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_sql' do
|
||||
describe 'when given a predicate' do
|
||||
it "manufactures sql with where clause conditions" do
|
||||
Selection.new(@relation, @predicate).to_sql.should be_like("
|
||||
Where.new(@relation, @predicate).to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`
|
||||
FROM `users`
|
||||
WHERE `users`.`id` = 1
|
||||
|
@ -28,7 +28,7 @@ module Arel
|
|||
|
||||
describe 'when given a string' do
|
||||
it "passes the string through to the where clause" do
|
||||
Selection.new(@relation, 'asdf').to_sql.should be_like("
|
||||
Where.new(@relation, 'asdf').to_sql.should be_like("
|
||||
SELECT `users`.`id`, `users`.`name`
|
||||
FROM `users`
|
||||
WHERE asdf
|
Loading…
Reference in a new issue