arel 3.0.3 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +2 -2
- data/Gemfile +3 -4
- data/History.txt +0 -51
- data/Manifest.txt +8 -2
- data/README.markdown +1 -1
- data/Rakefile +1 -1
- data/arel.gemspec +21 -29
- data/lib/arel.rb +1 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/nodes/and.rb +10 -0
- data/lib/arel/nodes/binary.rb +11 -0
- data/lib/arel/nodes/extract.rb +11 -0
- data/lib/arel/nodes/false.rb +7 -0
- data/lib/arel/nodes/function.rb +11 -0
- data/lib/arel/nodes/grouping.rb +7 -0
- data/lib/arel/nodes/insert_statement.rb +12 -0
- data/lib/arel/nodes/named_function.rb +9 -0
- data/lib/arel/nodes/select_core.rb +20 -0
- data/lib/arel/nodes/select_statement.rb +15 -1
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/terminal.rb +7 -0
- data/lib/arel/nodes/true.rb +7 -0
- data/lib/arel/nodes/unary.rb +10 -1
- data/lib/arel/nodes/update_statement.rb +15 -0
- data/lib/arel/nodes/window.rb +30 -3
- data/lib/arel/select_manager.rb +4 -0
- data/lib/arel/table.rb +13 -0
- data/lib/arel/tree_manager.rb +0 -2
- data/lib/arel/visitors/bind_visitor.rb +10 -0
- data/lib/arel/visitors/dot.rb +1 -1
- data/lib/arel/visitors/oracle.rb +1 -2
- data/lib/arel/visitors/to_sql.rb +138 -27
- data/test/nodes/test_and.rb +20 -0
- data/test/nodes/test_as.rb +12 -0
- data/test/nodes/test_ascending.rb +10 -0
- data/test/nodes/test_bin.rb +10 -0
- data/test/nodes/test_count.rb +12 -0
- data/test/nodes/test_delete_statement.rb +20 -0
- data/test/nodes/test_descending.rb +10 -0
- data/test/nodes/test_distinct.rb +20 -0
- data/test/nodes/test_equality.rb +10 -0
- data/test/nodes/test_extract.rb +14 -0
- data/test/nodes/test_false.rb +20 -0
- data/test/nodes/test_grouping.rb +25 -0
- data/test/nodes/test_infix_operation.rb +10 -0
- data/test/nodes/test_insert_statement.rb +24 -0
- data/test/nodes/test_named_function.rb +16 -0
- data/test/nodes/test_not.rb +12 -0
- data/test/nodes/test_or.rb +12 -0
- data/test/nodes/test_over.rb +18 -0
- data/test/nodes/test_select_core.rb +38 -0
- data/test/nodes/test_select_statement.rb +36 -0
- data/test/nodes/test_sql_literal.rb +10 -0
- data/test/nodes/test_sum.rb +12 -0
- data/test/nodes/test_table_alias.rb +36 -0
- data/test/nodes/test_true.rb +21 -0
- data/test/nodes/test_update_statement.rb +40 -0
- data/test/nodes/test_window.rb +73 -0
- data/test/test_attributes.rb +12 -0
- data/test/test_insert_manager.rb +0 -2
- data/test/test_select_manager.rb +10 -5
- data/test/test_table.rb +24 -0
- data/test/test_update_manager.rb +8 -0
- data/test/visitors/test_bind_visitor.rb +20 -1
- data/test/visitors/test_oracle.rb +1 -2
- data/test/visitors/test_to_sql.rb +44 -0
- metadata +76 -86
- data/lib/arel/nodes/ordering.rb +0 -6
- data/lib/arel/relation.rb +0 -6
data/lib/arel/nodes/terminal.rb
CHANGED
data/lib/arel/nodes/true.rb
CHANGED
data/lib/arel/nodes/unary.rb
CHANGED
@@ -7,12 +7,21 @@ module Arel
|
|
7
7
|
def initialize expr
|
8
8
|
@expr = expr
|
9
9
|
end
|
10
|
+
|
11
|
+
def hash
|
12
|
+
@expr.hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def eql? other
|
16
|
+
self.class == other.class &&
|
17
|
+
self.expr == other.expr
|
18
|
+
end
|
19
|
+
alias :== :eql?
|
10
20
|
end
|
11
21
|
|
12
22
|
%w{
|
13
23
|
Bin
|
14
24
|
Group
|
15
|
-
Grouping
|
16
25
|
Having
|
17
26
|
Limit
|
18
27
|
Not
|
@@ -18,6 +18,21 @@ module Arel
|
|
18
18
|
@wheres = @wheres.clone
|
19
19
|
@values = @values.clone
|
20
20
|
end
|
21
|
+
|
22
|
+
def hash
|
23
|
+
[@relation, @wheres, @values, @orders, @limit, @key].hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def eql? other
|
27
|
+
self.class == other.class &&
|
28
|
+
self.relation == other.relation &&
|
29
|
+
self.wheres == other.wheres &&
|
30
|
+
self.values == other.values &&
|
31
|
+
self.orders == other.orders &&
|
32
|
+
self.limit == other.limit &&
|
33
|
+
self.key == other.key
|
34
|
+
end
|
35
|
+
alias :== :eql?
|
21
36
|
end
|
22
37
|
end
|
23
38
|
end
|
data/lib/arel/nodes/window.rb
CHANGED
@@ -17,7 +17,6 @@ module Arel
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def frame(expr)
|
20
|
-
raise ArgumentError, "Window frame cannot be set more than once" if @frame
|
21
20
|
@framing = expr
|
22
21
|
end
|
23
22
|
|
@@ -33,6 +32,17 @@ module Arel
|
|
33
32
|
super
|
34
33
|
@orders = @orders.map { |x| x.clone }
|
35
34
|
end
|
35
|
+
|
36
|
+
def hash
|
37
|
+
[@orders, @framing].hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def eql? other
|
41
|
+
self.class == other.class &&
|
42
|
+
self.orders == other.orders &&
|
43
|
+
self.framing == other.framing
|
44
|
+
end
|
45
|
+
alias :== :eql?
|
36
46
|
end
|
37
47
|
|
38
48
|
class NamedWindow < Window
|
@@ -47,6 +57,15 @@ module Arel
|
|
47
57
|
super
|
48
58
|
@name = other.name.clone
|
49
59
|
end
|
60
|
+
|
61
|
+
def hash
|
62
|
+
super ^ @name.hash
|
63
|
+
end
|
64
|
+
|
65
|
+
def eql? other
|
66
|
+
super && self.name == other.name
|
67
|
+
end
|
68
|
+
alias :== :eql?
|
50
69
|
end
|
51
70
|
|
52
71
|
class Rows < Unary
|
@@ -61,7 +80,15 @@ module Arel
|
|
61
80
|
end
|
62
81
|
end
|
63
82
|
|
64
|
-
class CurrentRow <
|
83
|
+
class CurrentRow < Node
|
84
|
+
def hash
|
85
|
+
self.class.hash
|
86
|
+
end
|
87
|
+
|
88
|
+
def eql? other
|
89
|
+
self.class == other.class
|
90
|
+
end
|
91
|
+
end
|
65
92
|
|
66
93
|
class Preceding < Unary
|
67
94
|
def initialize(expr = nil)
|
@@ -75,4 +102,4 @@ module Arel
|
|
75
102
|
end
|
76
103
|
end
|
77
104
|
end
|
78
|
-
end
|
105
|
+
end
|
data/lib/arel/select_manager.rb
CHANGED
data/lib/arel/table.rb
CHANGED
@@ -123,6 +123,19 @@ Arel 4.0.0 with no replacement. PEW PEW PEW!!!
|
|
123
123
|
InsertManager.new(@engine)
|
124
124
|
end
|
125
125
|
|
126
|
+
def hash
|
127
|
+
[@name, @engine, @aliases, @table_alias].hash
|
128
|
+
end
|
129
|
+
|
130
|
+
def eql? other
|
131
|
+
self.class == other.class &&
|
132
|
+
self.name == other.name &&
|
133
|
+
self.engine == other.engine &&
|
134
|
+
self.aliases == other.aliases &&
|
135
|
+
self.table_alias == other.table_alias
|
136
|
+
end
|
137
|
+
alias :== :eql?
|
138
|
+
|
126
139
|
private
|
127
140
|
|
128
141
|
def attributes_for columns
|
data/lib/arel/tree_manager.rb
CHANGED
@@ -12,6 +12,15 @@ module Arel
|
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
|
+
|
16
|
+
def visit_Arel_Nodes_Assignment o
|
17
|
+
if o.right.is_a? Arel::Nodes::BindParam
|
18
|
+
"#{visit o.left} = #{visit o.right}"
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
def visit_Arel_Nodes_BindParam o
|
16
25
|
if @block
|
17
26
|
@block.call
|
@@ -19,6 +28,7 @@ module Arel
|
|
19
28
|
super
|
20
29
|
end
|
21
30
|
end
|
31
|
+
|
22
32
|
end
|
23
33
|
end
|
24
34
|
end
|
data/lib/arel/visitors/dot.rb
CHANGED
@@ -65,7 +65,6 @@ module Arel
|
|
65
65
|
visit_edge o, "expr"
|
66
66
|
end
|
67
67
|
alias :visit_Arel_Nodes_Group :unary
|
68
|
-
alias :visit_Arel_Nodes_BindParam :unary
|
69
68
|
alias :visit_Arel_Nodes_Grouping :unary
|
70
69
|
alias :visit_Arel_Nodes_Having :unary
|
71
70
|
alias :visit_Arel_Nodes_Limit :unary
|
@@ -195,6 +194,7 @@ module Arel
|
|
195
194
|
alias :visit_TrueClass :visit_String
|
196
195
|
alias :visit_FalseClass :visit_String
|
197
196
|
alias :visit_Arel_SqlLiteral :visit_String
|
197
|
+
alias :visit_Arel_Nodes_BindParam :visit_String
|
198
198
|
alias :visit_Fixnum :visit_String
|
199
199
|
alias :visit_BigDecimal :visit_String
|
200
200
|
alias :visit_Float :visit_String
|
data/lib/arel/visitors/oracle.rb
CHANGED
@@ -25,9 +25,8 @@ module Arel
|
|
25
25
|
SELECT * FROM (
|
26
26
|
SELECT raw_sql_.*, rownum raw_rnum_
|
27
27
|
FROM (#{sql}) raw_sql_
|
28
|
-
WHERE rownum <= #{offset.expr.to_i + limit}
|
29
28
|
)
|
30
|
-
WHERE #{
|
29
|
+
WHERE raw_rnum_ >= #{offset.expr.to_i + 1 } and rownum <= #{limit}
|
31
30
|
eosql
|
32
31
|
end
|
33
32
|
|
data/lib/arel/visitors/to_sql.rb
CHANGED
@@ -4,6 +4,55 @@ require 'date'
|
|
4
4
|
module Arel
|
5
5
|
module Visitors
|
6
6
|
class ToSql < Arel::Visitors::Visitor
|
7
|
+
##
|
8
|
+
# This is some roflscale crazy stuff. I'm roflscaling this because
|
9
|
+
# building SQL queries is a hotspot. I will explain the roflscale so that
|
10
|
+
# others will not rm this code.
|
11
|
+
#
|
12
|
+
# In YARV, string literals in a method body will get duped when the byte
|
13
|
+
# code is executed. Let's take a look:
|
14
|
+
#
|
15
|
+
# > puts RubyVM::InstructionSequence.new('def foo; "bar"; end').disasm
|
16
|
+
#
|
17
|
+
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>=====
|
18
|
+
# 0000 trace 8
|
19
|
+
# 0002 trace 1
|
20
|
+
# 0004 putstring "bar"
|
21
|
+
# 0006 trace 16
|
22
|
+
# 0008 leave
|
23
|
+
#
|
24
|
+
# The `putstring` bytecode will dup the string and push it on the stack.
|
25
|
+
# In many cases in our SQL visitor, that string is never mutated, so there
|
26
|
+
# is no need to dup the literal.
|
27
|
+
#
|
28
|
+
# If we change to a constant lookup, the string will not be duped, and we
|
29
|
+
# can reduce the objects in our system:
|
30
|
+
#
|
31
|
+
# > puts RubyVM::InstructionSequence.new('BAR = "bar"; def foo; BAR; end').disasm
|
32
|
+
#
|
33
|
+
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>========
|
34
|
+
# 0000 trace 8
|
35
|
+
# 0002 trace 1
|
36
|
+
# 0004 getinlinecache 11, <ic:0>
|
37
|
+
# 0007 getconstant :BAR
|
38
|
+
# 0009 setinlinecache <ic:0>
|
39
|
+
# 0011 trace 16
|
40
|
+
# 0013 leave
|
41
|
+
#
|
42
|
+
# `getconstant` should be a hash lookup, and no object is duped when the
|
43
|
+
# value of the constant is pushed on the stack. Hence the crazy
|
44
|
+
# constants below.
|
45
|
+
|
46
|
+
WHERE = ' WHERE ' # :nodoc:
|
47
|
+
SPACE = ' ' # :nodoc:
|
48
|
+
COMMA = ', ' # :nodoc:
|
49
|
+
GROUP_BY = ' GROUP BY ' # :nodoc:
|
50
|
+
ORDER_BY = ' ORDER BY ' # :nodoc:
|
51
|
+
WINDOW = ' WINDOW ' # :nodoc:
|
52
|
+
AND = ' AND ' # :nodoc:
|
53
|
+
|
54
|
+
DISTINCT = 'DISTINCT' # :nodoc:
|
55
|
+
|
7
56
|
attr_accessor :last_column
|
8
57
|
|
9
58
|
def initialize connection
|
@@ -23,7 +72,7 @@ module Arel
|
|
23
72
|
def visit_Arel_Nodes_DeleteStatement o
|
24
73
|
[
|
25
74
|
"DELETE FROM #{visit o.relation}",
|
26
|
-
("WHERE #{o.wheres.map { |x| visit x }.join
|
75
|
+
("WHERE #{o.wheres.map { |x| visit x }.join AND}" unless o.wheres.empty?)
|
27
76
|
].compact.join ' '
|
28
77
|
end
|
29
78
|
|
@@ -116,28 +165,80 @@ key on UpdateManager using UpdateManager#key=
|
|
116
165
|
end
|
117
166
|
|
118
167
|
def visit_Arel_Nodes_SelectStatement o
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
168
|
+
str = ''
|
169
|
+
|
170
|
+
if o.with
|
171
|
+
str << visit(o.with)
|
172
|
+
str << SPACE
|
173
|
+
end
|
174
|
+
|
175
|
+
o.cores.each { |x| str << visit_Arel_Nodes_SelectCore(x) }
|
176
|
+
|
177
|
+
unless o.orders.empty?
|
178
|
+
str << SPACE
|
179
|
+
str << ORDER_BY
|
180
|
+
len = o.orders.length - 1
|
181
|
+
o.orders.each_with_index { |x, i|
|
182
|
+
str << visit(x)
|
183
|
+
str << COMMA unless len == i
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
str << " #{visit(o.limit)}" if o.limit
|
188
|
+
str << " #{visit(o.offset)}" if o.offset
|
189
|
+
str << " #{visit(o.lock)}" if o.lock
|
190
|
+
|
191
|
+
str.strip!
|
192
|
+
str
|
127
193
|
end
|
128
194
|
|
129
195
|
def visit_Arel_Nodes_SelectCore o
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
196
|
+
str = "SELECT"
|
197
|
+
|
198
|
+
str << " #{visit(o.top)}" if o.top
|
199
|
+
str << " #{visit(o.set_quantifier)}" if o.set_quantifier
|
200
|
+
|
201
|
+
unless o.projections.empty?
|
202
|
+
str << SPACE
|
203
|
+
len = o.projections.length - 1
|
204
|
+
o.projections.each_with_index do |x, i|
|
205
|
+
str << visit(x)
|
206
|
+
str << COMMA unless len == i
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
str << " FROM #{visit(o.source)}" if o.source && !o.source.empty?
|
211
|
+
|
212
|
+
unless o.wheres.empty?
|
213
|
+
str << WHERE
|
214
|
+
len = o.wheres.length - 1
|
215
|
+
o.wheres.each_with_index do |x, i|
|
216
|
+
str << visit(x)
|
217
|
+
str << AND unless len == i
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
unless o.groups.empty?
|
222
|
+
str << GROUP_BY
|
223
|
+
len = o.groups.length - 1
|
224
|
+
o.groups.each_with_index do |x, i|
|
225
|
+
str << visit(x)
|
226
|
+
str << COMMA unless len == i
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
str << " #{visit(o.having)}" if o.having
|
231
|
+
|
232
|
+
unless o.windows.empty?
|
233
|
+
str << WINDOW
|
234
|
+
len = o.windows.length - 1
|
235
|
+
o.windows.each_with_index do |x, i|
|
236
|
+
str << visit(x)
|
237
|
+
str << COMMA unless len == i
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
str
|
141
242
|
end
|
142
243
|
|
143
244
|
def visit_Arel_Nodes_Bin o
|
@@ -145,7 +246,7 @@ key on UpdateManager using UpdateManager#key=
|
|
145
246
|
end
|
146
247
|
|
147
248
|
def visit_Arel_Nodes_Distinct o
|
148
|
-
|
249
|
+
DISTINCT
|
149
250
|
end
|
150
251
|
|
151
252
|
def visit_Arel_Nodes_DistinctOn o
|
@@ -254,6 +355,10 @@ key on UpdateManager using UpdateManager#key=
|
|
254
355
|
"(#{visit o.expr})"
|
255
356
|
end
|
256
357
|
|
358
|
+
def visit_Arel_SelectManager o
|
359
|
+
"(#{o.to_sql.rstrip})"
|
360
|
+
end
|
361
|
+
|
257
362
|
def visit_Arel_Nodes_Ascending o
|
258
363
|
"#{visit o.expr} ASC"
|
259
364
|
end
|
@@ -283,22 +388,22 @@ key on UpdateManager using UpdateManager#key=
|
|
283
388
|
end
|
284
389
|
|
285
390
|
def visit_Arel_Nodes_Sum o
|
286
|
-
"SUM(#{o.expressions.map { |x|
|
391
|
+
"SUM(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
287
392
|
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
288
393
|
end
|
289
394
|
|
290
395
|
def visit_Arel_Nodes_Max o
|
291
|
-
"MAX(#{o.expressions.map { |x|
|
396
|
+
"MAX(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
292
397
|
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
293
398
|
end
|
294
399
|
|
295
400
|
def visit_Arel_Nodes_Min o
|
296
|
-
"MIN(#{o.expressions.map { |x|
|
401
|
+
"MIN(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
297
402
|
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
298
403
|
end
|
299
404
|
|
300
405
|
def visit_Arel_Nodes_Avg o
|
301
|
-
"AVG(#{o.expressions.map { |x|
|
406
|
+
"AVG(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
302
407
|
visit x }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
303
408
|
end
|
304
409
|
|
@@ -350,7 +455,12 @@ key on UpdateManager using UpdateManager#key=
|
|
350
455
|
end
|
351
456
|
|
352
457
|
def visit_Arel_Nodes_InnerJoin o
|
353
|
-
"INNER JOIN #{visit o.left}
|
458
|
+
s = "INNER JOIN #{visit o.left}"
|
459
|
+
if o.right
|
460
|
+
s << SPACE
|
461
|
+
s << visit(o.right)
|
462
|
+
end
|
463
|
+
s
|
354
464
|
end
|
355
465
|
|
356
466
|
def visit_Arel_Nodes_On o
|
@@ -475,10 +585,11 @@ key on UpdateManager using UpdateManager#key=
|
|
475
585
|
alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
|
476
586
|
|
477
587
|
def visit_Array o
|
478
|
-
o.
|
588
|
+
o.map { |x| visit x }.join(', ')
|
479
589
|
end
|
480
590
|
|
481
591
|
def quote value, column = nil
|
592
|
+
return value if Arel::Nodes::SqlLiteral === value
|
482
593
|
@connection.quote value, column
|
483
594
|
end
|
484
595
|
|