arel 0.4.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/README.markdown +24 -0
  2. data/lib/arel.rb +3 -1
  3. data/lib/arel/algebra/attributes/attribute.rb +175 -141
  4. data/lib/arel/algebra/core_extensions.rb +0 -1
  5. data/lib/arel/algebra/core_extensions/hash.rb +5 -9
  6. data/lib/arel/algebra/core_extensions/object.rb +0 -4
  7. data/lib/arel/algebra/expression.rb +37 -24
  8. data/lib/arel/algebra/header.rb +5 -6
  9. data/lib/arel/algebra/ordering.rb +13 -5
  10. data/lib/arel/algebra/predicates.rb +143 -27
  11. data/lib/arel/algebra/relations.rb +0 -1
  12. data/lib/arel/algebra/relations/operations/from.rb +10 -2
  13. data/lib/arel/algebra/relations/operations/group.rb +8 -6
  14. data/lib/arel/algebra/relations/operations/having.rb +3 -6
  15. data/lib/arel/algebra/relations/operations/join.rb +52 -18
  16. data/lib/arel/algebra/relations/operations/lock.rb +4 -6
  17. data/lib/arel/algebra/relations/operations/order.rb +11 -7
  18. data/lib/arel/algebra/relations/operations/project.rb +10 -10
  19. data/lib/arel/algebra/relations/operations/skip.rb +10 -3
  20. data/lib/arel/algebra/relations/operations/take.rb +10 -3
  21. data/lib/arel/algebra/relations/operations/where.rb +12 -6
  22. data/lib/arel/algebra/relations/relation.rb +161 -92
  23. data/lib/arel/algebra/relations/row.rb +8 -5
  24. data/lib/arel/algebra/relations/utilities/compound.rb +34 -33
  25. data/lib/arel/algebra/relations/utilities/externalization.rb +10 -8
  26. data/lib/arel/algebra/relations/writes.rb +24 -13
  27. data/lib/arel/algebra/value.rb +41 -2
  28. data/lib/arel/engines/memory.rb +0 -2
  29. data/lib/arel/engines/memory/engine.rb +3 -9
  30. data/lib/arel/engines/memory/relations.rb +0 -3
  31. data/lib/arel/engines/memory/relations/array.rb +5 -3
  32. data/lib/arel/engines/memory/relations/operations.rb +2 -60
  33. data/lib/arel/engines/sql.rb +0 -2
  34. data/lib/arel/engines/sql/christener.rb +12 -6
  35. data/lib/arel/engines/sql/compilers/oracle_compiler.rb +34 -23
  36. data/lib/arel/engines/sql/compilers/postgresql_compiler.rb +23 -15
  37. data/lib/arel/engines/sql/engine.rb +19 -27
  38. data/lib/arel/engines/sql/formatters.rb +26 -10
  39. data/lib/arel/engines/sql/relations.rb +0 -7
  40. data/lib/arel/engines/sql/relations/compiler.rb +70 -35
  41. data/lib/arel/engines/sql/relations/table.rb +44 -32
  42. data/lib/arel/{engines/sql/relations/utilities/recursion.rb → recursion/base_case.rb} +0 -0
  43. data/lib/arel/session.rb +24 -40
  44. data/lib/arel/sql_literal.rb +13 -0
  45. data/lib/arel/version.rb +1 -1
  46. data/spec/algebra/unit/predicates/inequality_spec.rb +32 -0
  47. data/spec/algebra/unit/predicates/predicate_spec.rb +22 -0
  48. data/spec/algebra/unit/primitives/attribute_spec.rb +3 -9
  49. data/spec/algebra/unit/primitives/expression_spec.rb +1 -7
  50. data/spec/algebra/unit/relations/join_spec.rb +0 -7
  51. data/spec/algebra/unit/relations/project_spec.rb +3 -3
  52. data/spec/algebra/unit/relations/relation_spec.rb +74 -25
  53. data/spec/algebra/unit/session/session_spec.rb +7 -7
  54. data/spec/engines/memory/integration/joins/cross_engine_spec.rb +20 -10
  55. data/spec/engines/memory/unit/relations/array_spec.rb +6 -5
  56. data/spec/engines/memory/unit/relations/join_spec.rb +7 -6
  57. data/spec/engines/memory/unit/relations/order_spec.rb +7 -6
  58. data/spec/engines/memory/unit/relations/project_spec.rb +6 -6
  59. data/spec/engines/memory/unit/relations/skip_spec.rb +10 -5
  60. data/spec/engines/memory/unit/relations/take_spec.rb +7 -5
  61. data/spec/engines/memory/unit/relations/where_spec.rb +13 -9
  62. data/spec/engines/sql/unit/engine_spec.rb +20 -0
  63. data/spec/engines/sql/unit/relations/group_spec.rb +2 -2
  64. data/spec/engines/sql/unit/relations/order_spec.rb +5 -5
  65. data/spec/engines/sql/unit/relations/project_spec.rb +4 -4
  66. data/spec/engines/sql/unit/relations/table_spec.rb +0 -7
  67. data/spec/engines/sql/unit/relations/take_spec.rb +26 -0
  68. data/spec/engines/sql/unit/relations/where_spec.rb +1 -1
  69. data/spec/spec_helper.rb +1 -4
  70. data/spec/sql/christener_spec.rb +70 -0
  71. data/spec/support/model.rb +7 -2
  72. metadata +109 -23
  73. data/lib/arel/algebra/core_extensions/class.rb +0 -32
  74. data/lib/arel/algebra/relations/operations/alias.rb +0 -7
  75. data/lib/arel/engines/memory/predicates.rb +0 -99
  76. data/lib/arel/engines/memory/primitives.rb +0 -27
  77. data/lib/arel/engines/memory/relations/compound.rb +0 -9
  78. data/lib/arel/engines/memory/relations/writes.rb +0 -7
  79. data/lib/arel/engines/sql/predicates.rb +0 -103
  80. data/lib/arel/engines/sql/primitives.rb +0 -97
  81. data/lib/arel/engines/sql/relations/operations/alias.rb +0 -5
  82. data/lib/arel/engines/sql/relations/operations/join.rb +0 -33
  83. data/lib/arel/engines/sql/relations/relation.rb +0 -65
  84. data/lib/arel/engines/sql/relations/utilities/compound.rb +0 -10
  85. data/lib/arel/engines/sql/relations/utilities/externalization.rb +0 -14
  86. data/lib/arel/engines/sql/relations/writes.rb +0 -19
@@ -8,10 +8,6 @@ module Arel
8
8
  bind(relation)
9
9
  end
10
10
 
11
- def let
12
- yield(self)
13
- end
14
-
15
11
  Object.send(:include, self)
16
12
  end
17
13
  end
@@ -1,43 +1,56 @@
1
1
  module Arel
2
2
  class Expression < Attribute
3
- attributes :attribute, :alias, :ancestor
4
- deriving :==
5
- delegate :relation, :to => :attribute
6
- alias_method :name, :alias
3
+ attr_reader :attribute
4
+ alias :name :alias
7
5
 
8
6
  def initialize(attribute, aliaz = nil, ancestor = nil)
9
- @attribute, @alias, @ancestor = attribute, aliaz, ancestor
7
+ super(attribute.relation, aliaz, :alias => aliaz, :ancestor => ancestor)
8
+ @attribute = attribute
10
9
  end
11
10
 
12
11
  def aggregation?
13
12
  true
14
13
  end
15
14
 
16
- def inspect
17
- "<#{self.class.name} #{attribute.inspect}>"
15
+ def to_sql(formatter = Sql::SelectClause.new(relation))
16
+ formatter.expression self
18
17
  end
19
18
 
20
- module Transformations
21
- def as(aliaz)
22
- self.class.new(attribute, aliaz, self)
23
- end
19
+ def as(aliaz)
20
+ self.class.new(attribute, aliaz, self)
21
+ end
24
22
 
25
- def bind(new_relation)
26
- new_relation == relation ? self : self.class.new(attribute.bind(new_relation), @alias, self)
27
- end
23
+ def bind(new_relation)
24
+ new_relation == relation ? self : self.class.new(attribute.bind(new_relation), @alias, self)
25
+ end
28
26
 
29
- def to_attribute(relation)
30
- Attribute.new(relation, @alias, :ancestor => self)
31
- end
27
+ def to_attribute(relation)
28
+ Attribute.new(relation, @alias, :ancestor => self)
32
29
  end
33
- include Transformations
34
30
  end
35
31
 
36
- class Count < Expression; end
37
- class Distinct < Expression; end
38
- class Sum < Expression; end
39
- class Maximum < Expression; end
40
- class Minimum < Expression; end
41
- class Average < Expression; end
32
+ class Count < Expression
33
+ def function_sql; 'COUNT' end
34
+ end
35
+
36
+ class Distinct < Expression
37
+ def function_sql; 'DISTINCT' end
38
+ end
39
+
40
+ class Sum < Expression
41
+ def function_sql; 'SUM' end
42
+ end
43
+
44
+ class Maximum < Expression
45
+ def function_sql; 'MAX' end
46
+ end
47
+
48
+ class Minimum < Expression
49
+ def function_sql; 'MIN' end
50
+ end
51
+
52
+ class Average < Expression
53
+ def function_sql; 'AVG' end
54
+ end
42
55
  end
43
56
 
@@ -4,13 +4,11 @@ module Arel
4
4
 
5
5
  def initialize(attrs = [])
6
6
  @attributes = attrs.to_ary
7
- @names = Hash.new do |h,k|
8
- h[k] = @attributes.detect { |a| a.named?(k) }
9
- end
7
+ @names = {}
10
8
  end
11
9
 
12
- def each(&block)
13
- to_ary.each(&block)
10
+ def each
11
+ to_ary.each { |e| yield e }
14
12
  self
15
13
  end
16
14
 
@@ -55,7 +53,8 @@ module Arel
55
53
  end
56
54
 
57
55
  def find_by_name(name)
58
- @names[name.to_sym]
56
+ k = name.to_sym
57
+ @names[k] ||= @attributes.detect { |a| a.named?(k) }
59
58
  end
60
59
 
61
60
  def find_by_attribute(attr)
@@ -1,5 +1,5 @@
1
1
  module Arel
2
- class Ordering
2
+ class Ordering < Struct.new(:attribute)
3
3
  delegate :relation, :to => :attribute
4
4
 
5
5
  def bind(relation)
@@ -9,15 +9,23 @@ module Arel
9
9
  def to_ordering
10
10
  self
11
11
  end
12
+
13
+ def eval(row1, row2)
14
+ (attribute.eval(row1) <=> attribute.eval(row2)) * direction
15
+ end
16
+
17
+ def to_sql(formatter = Sql::OrderClause.new(relation))
18
+ formatter.ordering self
19
+ end
12
20
  end
13
21
 
14
22
  class Ascending < Ordering
15
- attributes :attribute
16
- deriving :initialize, :==
23
+ def direction; 1 end
24
+ def direction_sql; 'ASC' end
17
25
  end
18
26
 
19
27
  class Descending < Ordering
20
- attributes :attribute
21
- deriving :initialize, :==
28
+ def direction_sql; 'DESC' end
29
+ def direction; -1 end
22
30
  end
23
31
  end
@@ -19,7 +19,7 @@ module Arel
19
19
  end
20
20
 
21
21
  class Polyadic < Predicate
22
- attributes :predicates
22
+ attr_reader :predicates
23
23
 
24
24
  def initialize(*predicates)
25
25
  @predicates = predicates
@@ -32,14 +32,14 @@ module Arel
32
32
  # * <tt>additional_operands</tt> - All possible right-hand operands
33
33
  def self.build(operator, operand1, *additional_operands)
34
34
  new(
35
- *additional_operands.uniq.inject([]) do |predicates, operand|
36
- predicates << operator.new(operand1, operand)
35
+ *additional_operands.uniq.map do |operand|
36
+ operator.new(operand1, operand)
37
37
  end
38
38
  )
39
39
  end
40
40
 
41
41
  def ==(other)
42
- same_elements?(@predicates, other.predicates)
42
+ super || predicates == other.predicates
43
43
  end
44
44
 
45
45
  def bind(relation)
@@ -48,14 +48,16 @@ module Arel
48
48
  )
49
49
  end
50
50
 
51
- private
52
-
53
- def same_elements?(a1, a2)
54
- [:select, :inject, :size].each do |m|
55
- return false unless [a1, a2].each {|a| a.respond_to?(m) }
51
+ def eval(row)
52
+ predicates.send(compounder) do |operation|
53
+ operation.eval(row)
56
54
  end
57
- a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h } ==
58
- a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
55
+ end
56
+
57
+ def to_sql(formatter = nil)
58
+ "(" +
59
+ predicates.map {|p| p.to_sql(formatter)}.join(" #{predicate_sql} ") +
60
+ ")"
59
61
  end
60
62
  end
61
63
 
@@ -63,61 +65,118 @@ module Arel
63
65
  def complement
64
66
  All.new(*predicates.map {|p| p.complement})
65
67
  end
68
+
69
+ def compounder; :any? end
70
+
71
+ def predicate_sql; "OR" end
66
72
  end
67
73
 
68
74
  class All < Polyadic
69
75
  def complement
70
76
  Any.new(*predicates.map {|p| p.complement})
71
77
  end
78
+
79
+ def compounder; :all? end
80
+
81
+ def predicate_sql; "AND" end
72
82
  end
73
83
 
74
84
  class Unary < Predicate
75
- attributes :operand
76
- deriving :initialize, :==
85
+ attr_reader :operand
86
+
87
+ def initialize operand
88
+ @operand = operand
89
+ end
77
90
 
78
91
  def bind(relation)
79
92
  self.class.new(operand.find_correlate_in(relation))
80
93
  end
94
+
95
+ def == other
96
+ super || self.class === other && operand == other.operand
97
+ end
98
+
99
+ def eval(row)
100
+ operand.eval(row).send(operator)
101
+ end
102
+
103
+ def to_sql(formatter = nil)
104
+ "#{predicate_sql} (#{operand.to_sql(formatter)})"
105
+ end
81
106
  end
82
107
 
83
108
  class Not < Unary
84
109
  def complement
85
110
  operand
86
111
  end
112
+
113
+ def eval(row)
114
+ !operand.eval(row)
115
+ end
116
+
117
+ def predicate_sql; "NOT" end
87
118
  end
88
119
 
89
- class Binary < Predicate
90
- attributes :operand1, :operand2
91
- deriving :initialize
120
+ class Binary < Unary
121
+ attr_reader :operand2
122
+ alias :operand1 :operand
123
+
124
+ def initialize left, right
125
+ super(left)
126
+ @operand2 = right
127
+ end
92
128
 
93
129
  def ==(other)
94
- self.class === other and
95
- @operand1 == other.operand1 and
96
- @operand2 == other.operand2
130
+ super && @operand2 == other.operand2
97
131
  end
98
132
 
99
133
  def bind(relation)
100
134
  self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
101
135
  end
136
+
137
+ def eval(row)
138
+ operand1.eval(row).send(operator, operand2.eval(row))
139
+ end
140
+
141
+ def to_sql(formatter = nil)
142
+ "#{operand1.to_sql} #{predicate_sql} #{operand1.format(operand2)}"
143
+ end
144
+ alias :value :to_sql
102
145
  end
103
146
 
104
- class CompoundPredicate < Binary; end
147
+ class CompoundPredicate < Binary
148
+ def eval(row)
149
+ eval "operand1.eval(row) #{operator} operand2.eval(row)"
150
+ end
151
+
152
+ def to_sql(formatter = nil)
153
+ "(#{operand1.to_sql(formatter)} #{predicate_sql} #{operand2.to_sql(formatter)})"
154
+ end
155
+ end
105
156
 
106
157
  class And < CompoundPredicate
107
158
  def complement
108
159
  Or.new(operand1.complement, operand2.complement)
109
160
  end
161
+
162
+ def operator; :and end
163
+
164
+ def predicate_sql; "AND" end
110
165
  end
111
166
 
112
167
  class Or < CompoundPredicate
113
168
  def complement
114
169
  And.new(operand1.complement, operand2.complement)
115
170
  end
171
+
172
+ def operator; :or end
173
+
174
+ def predicate_sql; "OR" end
116
175
  end
117
176
 
118
177
  class Equality < Binary
119
178
  def ==(other)
120
- Equality === other and
179
+ self.class === other and
121
180
  ((operand1 == other.operand1 and operand2 == other.operand2) or
122
181
  (operand1 == other.operand2 and operand2 == other.operand1))
123
182
  end
@@ -125,66 +184,123 @@ module Arel
125
184
  def complement
126
185
  Inequality.new(operand1, operand2)
127
186
  end
128
- end
129
187
 
130
- class Inequality < Binary
131
- def ==(other)
132
- Equality === other and
133
- ((operand1 == other.operand1 and operand2 == other.operand2) or
134
- (operand1 == other.operand2 and operand2 == other.operand1))
188
+ def operator; :== end
189
+
190
+ def predicate_sql
191
+ operand2.equality_predicate_sql
135
192
  end
193
+ end
136
194
 
195
+ class Inequality < Equality
137
196
  def complement
138
197
  Equality.new(operand1, operand2)
139
198
  end
199
+
200
+ def operator; :"!=" end
201
+ def eval(row)
202
+ operand1.eval(row) != operand2.eval(row)
203
+ end
204
+
205
+ def predicate_sql
206
+ operand2.inequality_predicate_sql
207
+ end
140
208
  end
141
209
 
142
210
  class GreaterThanOrEqualTo < Binary
143
211
  def complement
144
212
  LessThan.new(operand1, operand2)
145
213
  end
214
+
215
+ def operator; :>= end
216
+
217
+ def predicate_sql; '>=' end
146
218
  end
147
219
 
148
220
  class GreaterThan < Binary
149
221
  def complement
150
222
  LessThanOrEqualTo.new(operand1, operand2)
151
223
  end
224
+
225
+ def operator; :> end
226
+
227
+ def predicate_sql; '>' end
152
228
  end
153
229
 
154
230
  class LessThanOrEqualTo < Binary
155
231
  def complement
156
232
  GreaterThan.new(operand1, operand2)
157
233
  end
234
+
235
+ def operator; :<= end
236
+
237
+ def predicate_sql; '<=' end
158
238
  end
159
239
 
160
240
  class LessThan < Binary
161
241
  def complement
162
242
  GreaterThanOrEqualTo.new(operand1, operand2)
163
243
  end
244
+
245
+ def operator; :< end
246
+
247
+ def predicate_sql; '<' end
164
248
  end
165
249
 
166
250
  class Match < Binary
167
251
  def complement
168
252
  NotMatch.new(operand1, operand2)
169
253
  end
254
+
255
+ def operator; :=~ end
256
+
257
+ def predicate_sql; 'LIKE' end
170
258
  end
171
259
 
172
260
  class NotMatch < Binary
173
261
  def complement
174
262
  Match.new(operand1, operand2)
175
263
  end
264
+
265
+ def eval(row)
266
+ operand1.eval(row) !~ operand2.eval(row)
267
+ end
268
+
269
+ def predicate_sql; 'NOT LIKE' end
176
270
  end
177
271
 
178
272
  class In < Binary
179
273
  def complement
180
274
  NotIn.new(operand1, operand2)
181
275
  end
276
+
277
+ def eval(row)
278
+ operand2.eval(row).include?(operand1.eval(row))
279
+ end
280
+
281
+ def to_sql(formatter = nil)
282
+ if operand2.is_a?(Range) && operand2.exclude_end?
283
+ GreaterThanOrEqualTo.new(operand1, operand2.begin).and(
284
+ LessThan.new(operand1, operand2.end)
285
+ ).to_sql(formatter)
286
+ else
287
+ super
288
+ end
289
+ end
290
+
291
+ def predicate_sql; operand2.inclusion_predicate_sql end
182
292
  end
183
293
 
184
294
  class NotIn < Binary
185
295
  def complement
186
296
  In.new(operand1, operand2)
187
297
  end
298
+
299
+ def eval(row)
300
+ !(operand2.eval(row).include?(operand1.eval(row)))
301
+ end
302
+
303
+ def predicate_sql; operand2.exclusion_predicate_sql end
188
304
  end
189
305
  end
190
306
  end