arel 0.4.0 → 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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