arel 6.0.4 → 7.1.4
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.
- checksums.yaml +4 -4
- data/History.txt +31 -12
- data/MIT-LICENSE.txt +2 -1
- data/{README.markdown → README.md} +88 -31
- data/lib/arel.rb +1 -1
- data/lib/arel/attributes/attribute.rb +8 -0
- data/lib/arel/crud.rb +4 -3
- data/lib/arel/delete_manager.rb +6 -1
- data/lib/arel/insert_manager.rb +1 -1
- data/lib/arel/math.rb +24 -0
- data/lib/arel/nodes.rb +6 -38
- data/lib/arel/nodes/binary.rb +0 -2
- data/lib/arel/nodes/bind_param.rb +3 -0
- data/lib/arel/nodes/case.rb +57 -0
- data/lib/arel/nodes/casted.rb +44 -0
- data/lib/arel/nodes/delete_statement.rb +2 -0
- data/lib/arel/nodes/infix_operation.rb +36 -1
- data/lib/arel/nodes/matches.rb +3 -1
- data/lib/arel/nodes/regexp.rb +14 -0
- data/lib/arel/nodes/select_core.rb +5 -5
- data/lib/arel/nodes/table_alias.rb +6 -2
- data/lib/arel/nodes/unary.rb +6 -3
- data/lib/arel/nodes/unary_operation.rb +25 -0
- data/lib/arel/predications.rb +38 -14
- data/lib/arel/select_manager.rb +6 -6
- data/lib/arel/table.rb +34 -59
- data/lib/arel/tree_manager.rb +3 -8
- data/lib/arel/update_manager.rb +1 -1
- data/lib/arel/visitors.rb +1 -23
- data/lib/arel/visitors/depth_first.rb +14 -2
- data/lib/arel/visitors/dot.rb +12 -1
- data/lib/arel/visitors/informix.rb +6 -1
- data/lib/arel/visitors/mssql.rb +35 -3
- data/lib/arel/visitors/mysql.rb +8 -0
- data/lib/arel/visitors/oracle.rb +13 -2
- data/lib/arel/visitors/oracle12.rb +59 -0
- data/lib/arel/visitors/postgresql.rb +56 -4
- data/lib/arel/visitors/sqlite.rb +9 -0
- data/lib/arel/visitors/to_sql.rb +94 -52
- data/lib/arel/visitors/where_sql.rb +10 -1
- metadata +11 -6
@@ -0,0 +1,59 @@
|
|
1
|
+
module Arel
|
2
|
+
module Visitors
|
3
|
+
class Oracle12 < Arel::Visitors::ToSql
|
4
|
+
private
|
5
|
+
|
6
|
+
def visit_Arel_Nodes_SelectStatement o, collector
|
7
|
+
# Oracle does not allow LIMIT clause with select for update
|
8
|
+
if o.limit && o.lock
|
9
|
+
raise ArgumentError, <<-MSG
|
10
|
+
'Combination of limit and lock is not supported.
|
11
|
+
because generated SQL statements
|
12
|
+
`SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.`
|
13
|
+
MSG
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_Arel_Nodes_SelectOptions o, collector
|
19
|
+
collector = maybe_visit o.offset, collector
|
20
|
+
collector = maybe_visit o.limit, collector
|
21
|
+
collector = maybe_visit o.lock, collector
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_Arel_Nodes_Limit o, collector
|
25
|
+
collector << "FETCH FIRST "
|
26
|
+
collector = visit o.expr, collector
|
27
|
+
collector << " ROWS ONLY"
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Arel_Nodes_Offset o, collector
|
31
|
+
collector << "OFFSET "
|
32
|
+
visit o.expr, collector
|
33
|
+
collector << " ROWS"
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_Arel_Nodes_Except o, collector
|
37
|
+
collector << "( "
|
38
|
+
collector = infix_value o, collector, " MINUS "
|
39
|
+
collector << " )"
|
40
|
+
end
|
41
|
+
|
42
|
+
def visit_Arel_Nodes_UpdateStatement o, collector
|
43
|
+
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
|
44
|
+
if o.orders.any? && o.limit.nil?
|
45
|
+
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
|
46
|
+
# otherwise let the user deal with the error
|
47
|
+
o = o.dup
|
48
|
+
o.orders = []
|
49
|
+
end
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_Arel_Nodes_BindParam o, collector
|
55
|
+
collector.add_bind(o) { |i| ":a#{i}" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,22 +1,42 @@
|
|
1
1
|
module Arel
|
2
2
|
module Visitors
|
3
3
|
class PostgreSQL < Arel::Visitors::ToSql
|
4
|
+
CUBE = 'CUBE'
|
5
|
+
ROLLUP = 'ROLLUP'
|
6
|
+
GROUPING_SET = 'GROUPING SET'
|
7
|
+
|
4
8
|
private
|
5
9
|
|
6
10
|
def visit_Arel_Nodes_Matches o, collector
|
7
|
-
|
11
|
+
op = o.case_sensitive ? ' LIKE ' : ' ILIKE '
|
12
|
+
collector = infix_value o, collector, op
|
13
|
+
if o.escape
|
14
|
+
collector << ' ESCAPE '
|
15
|
+
visit o.escape, collector
|
16
|
+
else
|
17
|
+
collector
|
18
|
+
end
|
8
19
|
end
|
9
20
|
|
10
21
|
def visit_Arel_Nodes_DoesNotMatch o, collector
|
11
|
-
|
22
|
+
op = o.case_sensitive ? ' NOT LIKE ' : ' NOT ILIKE '
|
23
|
+
collector = infix_value o, collector, op
|
24
|
+
if o.escape
|
25
|
+
collector << ' ESCAPE '
|
26
|
+
visit o.escape, collector
|
27
|
+
else
|
28
|
+
collector
|
29
|
+
end
|
12
30
|
end
|
13
31
|
|
14
32
|
def visit_Arel_Nodes_Regexp o, collector
|
15
|
-
|
33
|
+
op = o.case_sensitive ? ' ~ ' : ' ~* '
|
34
|
+
infix_value o, collector, op
|
16
35
|
end
|
17
36
|
|
18
37
|
def visit_Arel_Nodes_NotRegexp o, collector
|
19
|
-
|
38
|
+
op = o.case_sensitive ? ' !~ ' : ' !~* '
|
39
|
+
infix_value o, collector, op
|
20
40
|
end
|
21
41
|
|
22
42
|
def visit_Arel_Nodes_DistinctOn o, collector
|
@@ -27,6 +47,38 @@ module Arel
|
|
27
47
|
def visit_Arel_Nodes_BindParam o, collector
|
28
48
|
collector.add_bind(o) { |i| "$#{i}" }
|
29
49
|
end
|
50
|
+
|
51
|
+
def visit_Arel_Nodes_GroupingElement o, collector
|
52
|
+
collector << "( "
|
53
|
+
visit(o.expr, collector) << " )"
|
54
|
+
end
|
55
|
+
|
56
|
+
def visit_Arel_Nodes_Cube o, collector
|
57
|
+
collector << CUBE
|
58
|
+
grouping_array_or_grouping_element o, collector
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_Arel_Nodes_RollUp o, collector
|
62
|
+
collector << ROLLUP
|
63
|
+
grouping_array_or_grouping_element o, collector
|
64
|
+
end
|
65
|
+
|
66
|
+
def visit_Arel_Nodes_GroupingSet o, collector
|
67
|
+
collector << GROUPING_SET
|
68
|
+
grouping_array_or_grouping_element o, collector
|
69
|
+
end
|
70
|
+
|
71
|
+
# Utilized by GroupingSet, Cube & RollUp visitors to
|
72
|
+
# handle grouping aggregation semantics
|
73
|
+
def grouping_array_or_grouping_element o, collector
|
74
|
+
if o.expr.is_a? Array
|
75
|
+
collector << "( "
|
76
|
+
visit o.expr, collector
|
77
|
+
collector << " )"
|
78
|
+
else
|
79
|
+
visit o.expr, collector
|
80
|
+
end
|
81
|
+
end
|
30
82
|
end
|
31
83
|
end
|
32
84
|
end
|
data/lib/arel/visitors/sqlite.rb
CHANGED
@@ -12,6 +12,15 @@ module Arel
|
|
12
12
|
o.limit = Arel::Nodes::Limit.new(-1) if o.offset && !o.limit
|
13
13
|
super
|
14
14
|
end
|
15
|
+
|
16
|
+
def visit_Arel_Nodes_True o, collector
|
17
|
+
collector << "1"
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Arel_Nodes_False o, collector
|
21
|
+
collector << "0"
|
22
|
+
end
|
23
|
+
|
15
24
|
end
|
16
25
|
end
|
17
26
|
end
|
data/lib/arel/visitors/to_sql.rb
CHANGED
@@ -4,6 +4,12 @@ require 'arel/visitors/reduce'
|
|
4
4
|
|
5
5
|
module Arel
|
6
6
|
module Visitors
|
7
|
+
class UnsupportedVisitError < StandardError
|
8
|
+
def initialize(object)
|
9
|
+
super "Unsupported argument type: #{object.class.name}. Construct an Arel node instead."
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
class ToSql < Arel::Visitors::Reduce
|
8
14
|
##
|
9
15
|
# This is some roflscale crazy stuff. I'm roflscaling this because
|
@@ -74,14 +80,14 @@ module Arel
|
|
74
80
|
end
|
75
81
|
|
76
82
|
def visit_Arel_Nodes_DeleteStatement o, collector
|
77
|
-
collector <<
|
83
|
+
collector << 'DELETE FROM '
|
78
84
|
collector = visit o.relation, collector
|
79
85
|
if o.wheres.any?
|
80
|
-
collector <<
|
81
|
-
inject_join o.wheres, collector, AND
|
82
|
-
else
|
83
|
-
collector
|
86
|
+
collector << ' WHERE '
|
87
|
+
collector = inject_join o.wheres, collector, AND
|
84
88
|
end
|
89
|
+
|
90
|
+
maybe_visit o.limit, collector
|
85
91
|
end
|
86
92
|
|
87
93
|
# FIXME: we should probably have a 2-pass visitor for this
|
@@ -164,7 +170,7 @@ module Arel
|
|
164
170
|
end
|
165
171
|
|
166
172
|
def table_exists? name
|
167
|
-
schema_cache.
|
173
|
+
schema_cache.data_source_exists?(name)
|
168
174
|
end
|
169
175
|
|
170
176
|
def column_for attr
|
@@ -193,7 +199,7 @@ module Arel
|
|
193
199
|
collector << quote(value, attr && column_for(attr)).to_s
|
194
200
|
end
|
195
201
|
unless i == len
|
196
|
-
collector <<
|
202
|
+
collector << COMMA
|
197
203
|
end
|
198
204
|
}
|
199
205
|
|
@@ -211,7 +217,6 @@ module Arel
|
|
211
217
|
}
|
212
218
|
|
213
219
|
unless o.orders.empty?
|
214
|
-
collector << SPACE
|
215
220
|
collector << ORDER_BY
|
216
221
|
len = o.orders.length - 1
|
217
222
|
o.orders.each_with_index { |x, i|
|
@@ -220,11 +225,15 @@ module Arel
|
|
220
225
|
}
|
221
226
|
end
|
222
227
|
|
228
|
+
visit_Arel_Nodes_SelectOptions(o, collector)
|
229
|
+
|
230
|
+
collector
|
231
|
+
end
|
232
|
+
|
233
|
+
def visit_Arel_Nodes_SelectOptions o, collector
|
223
234
|
collector = maybe_visit o.limit, collector
|
224
235
|
collector = maybe_visit o.offset, collector
|
225
236
|
collector = maybe_visit o.lock, collector
|
226
|
-
|
227
|
-
collector
|
228
237
|
end
|
229
238
|
|
230
239
|
def visit_Arel_Nodes_SelectCore o, collector
|
@@ -234,50 +243,33 @@ module Arel
|
|
234
243
|
|
235
244
|
collector = maybe_visit o.set_quantifier, collector
|
236
245
|
|
237
|
-
|
238
|
-
collector << SPACE
|
239
|
-
len = o.projections.length - 1
|
240
|
-
o.projections.each_with_index do |x, i|
|
241
|
-
collector = visit(x, collector)
|
242
|
-
collector << COMMA unless len == i
|
243
|
-
end
|
244
|
-
end
|
246
|
+
collect_nodes_for o.projections, collector, SPACE
|
245
247
|
|
246
248
|
if o.source && !o.source.empty?
|
247
249
|
collector << " FROM "
|
248
250
|
collector = visit o.source, collector
|
249
251
|
end
|
250
252
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
collector << AND unless len == i
|
257
|
-
end
|
253
|
+
collect_nodes_for o.wheres, collector, WHERE, AND
|
254
|
+
collect_nodes_for o.groups, collector, GROUP_BY
|
255
|
+
unless o.havings.empty?
|
256
|
+
collector << " HAVING "
|
257
|
+
inject_join o.havings, collector, AND
|
258
258
|
end
|
259
|
+
collect_nodes_for o.windows, collector, WINDOW
|
259
260
|
|
260
|
-
|
261
|
-
|
262
|
-
len = o.groups.length - 1
|
263
|
-
o.groups.each_with_index do |x, i|
|
264
|
-
collector = visit(x, collector)
|
265
|
-
collector << COMMA unless len == i
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
collector = maybe_visit o.having, collector
|
261
|
+
collector
|
262
|
+
end
|
270
263
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
264
|
+
def collect_nodes_for nodes, collector, spacer, connector = COMMA
|
265
|
+
unless nodes.empty?
|
266
|
+
collector << spacer
|
267
|
+
len = nodes.length - 1
|
268
|
+
nodes.each_with_index do |x, i|
|
275
269
|
collector = visit(x, collector)
|
276
|
-
collector <<
|
270
|
+
collector << connector unless len == i
|
277
271
|
end
|
278
272
|
end
|
279
|
-
|
280
|
-
collector
|
281
273
|
end
|
282
274
|
|
283
275
|
def visit_Arel_Nodes_Bin o, collector
|
@@ -337,13 +329,13 @@ module Arel
|
|
337
329
|
end
|
338
330
|
|
339
331
|
if o.orders.any?
|
340
|
-
collector <<
|
332
|
+
collector << SPACE if o.partitions.any?
|
341
333
|
collector << "ORDER BY "
|
342
334
|
collector = inject_join o.orders, collector, ", "
|
343
335
|
end
|
344
336
|
|
345
337
|
if o.framing
|
346
|
-
collector <<
|
338
|
+
collector << SPACE if o.partitions.any? or o.orders.any?
|
347
339
|
collector = visit o.framing, collector
|
348
340
|
end
|
349
341
|
|
@@ -405,11 +397,6 @@ module Arel
|
|
405
397
|
end
|
406
398
|
end
|
407
399
|
|
408
|
-
def visit_Arel_Nodes_Having o, collector
|
409
|
-
collector << "HAVING "
|
410
|
-
visit o.expr, collector
|
411
|
-
end
|
412
|
-
|
413
400
|
def visit_Arel_Nodes_Offset o, collector
|
414
401
|
collector << "OFFSET "
|
415
402
|
visit o.expr, collector
|
@@ -557,7 +544,7 @@ module Arel
|
|
557
544
|
collector = visit o.left, collector
|
558
545
|
end
|
559
546
|
if o.right.any?
|
560
|
-
collector <<
|
547
|
+
collector << SPACE if o.left
|
561
548
|
collector = inject_join o.right, collector, ' '
|
562
549
|
end
|
563
550
|
collector
|
@@ -701,6 +688,35 @@ module Arel
|
|
701
688
|
visit o.right, collector
|
702
689
|
end
|
703
690
|
|
691
|
+
def visit_Arel_Nodes_Case o, collector
|
692
|
+
collector << "CASE "
|
693
|
+
if o.case
|
694
|
+
visit o.case, collector
|
695
|
+
collector << " "
|
696
|
+
end
|
697
|
+
o.conditions.each do |condition|
|
698
|
+
visit condition, collector
|
699
|
+
collector << " "
|
700
|
+
end
|
701
|
+
if o.default
|
702
|
+
visit o.default, collector
|
703
|
+
collector << " "
|
704
|
+
end
|
705
|
+
collector << "END"
|
706
|
+
end
|
707
|
+
|
708
|
+
def visit_Arel_Nodes_When o, collector
|
709
|
+
collector << "WHEN "
|
710
|
+
visit o.left, collector
|
711
|
+
collector << " THEN "
|
712
|
+
visit o.right, collector
|
713
|
+
end
|
714
|
+
|
715
|
+
def visit_Arel_Nodes_Else o, collector
|
716
|
+
collector << "ELSE "
|
717
|
+
visit o.expr, collector
|
718
|
+
end
|
719
|
+
|
704
720
|
def visit_Arel_Nodes_UnqualifiedColumn o, collector
|
705
721
|
collector << "#{quote_column_name o.name}"
|
706
722
|
collector
|
@@ -729,11 +745,15 @@ module Arel
|
|
729
745
|
alias :visit_Integer :literal
|
730
746
|
|
731
747
|
def quoted o, a
|
732
|
-
|
748
|
+
if a && a.able_to_type_cast?
|
749
|
+
quote(a.type_cast_for_database(o))
|
750
|
+
else
|
751
|
+
quote(o, column_for(a))
|
752
|
+
end
|
733
753
|
end
|
734
754
|
|
735
755
|
def unsupported o, collector
|
736
|
-
raise
|
756
|
+
raise UnsupportedVisitError.new(o)
|
737
757
|
end
|
738
758
|
|
739
759
|
alias :visit_ActiveSupport_Multibyte_Chars :unsupported
|
@@ -762,6 +782,11 @@ module Arel
|
|
762
782
|
alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
|
763
783
|
alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
|
764
784
|
|
785
|
+
def visit_Arel_Nodes_UnaryOperation o, collector
|
786
|
+
collector << " #{o.operator} "
|
787
|
+
visit o.expr, collector
|
788
|
+
end
|
789
|
+
|
765
790
|
def visit_Array o, collector
|
766
791
|
inject_join o, collector, ", "
|
767
792
|
end
|
@@ -769,6 +794,9 @@ module Arel
|
|
769
794
|
|
770
795
|
def quote value, column = nil
|
771
796
|
return value if Arel::Nodes::SqlLiteral === value
|
797
|
+
if column
|
798
|
+
print_type_cast_deprecation
|
799
|
+
end
|
772
800
|
@connection.quote value, column
|
773
801
|
end
|
774
802
|
|
@@ -818,6 +846,20 @@ module Arel
|
|
818
846
|
collector
|
819
847
|
end
|
820
848
|
end
|
849
|
+
|
850
|
+
def print_type_cast_deprecation
|
851
|
+
unless defined?($arel_silence_type_casting_deprecation) && $arel_silence_type_casting_deprecation
|
852
|
+
warn <<-eowarn
|
853
|
+
Arel performing automatic type casting is deprecated, and will be removed in Arel 8.0. If you are seeing this, it is because you are manually passing a value to an Arel predicate, and the `Arel::Table` object was constructed manually. The easiest way to remove this warning is to use an `Arel::Table` object returned from calling `arel_table` on an ActiveRecord::Base subclass.
|
854
|
+
|
855
|
+
If you're certain the value is already of the right type, change `attribute.eq(value)` to `attribute.eq(Arel::Nodes::Quoted.new(value))` (you will be able to remove that in Arel 8.0, it is only required to silence this deprecation warning).
|
856
|
+
|
857
|
+
You can also silence this warning globally by setting `$arel_silence_type_casting_deprecation` to `true`. (Do NOT do this if you are a library author)
|
858
|
+
|
859
|
+
If you are passing user input to a predicate, you must either give an appropriate type caster object to the `Arel::Table`, or manually cast the value before passing it to Arel.
|
860
|
+
eowarn
|
861
|
+
end
|
862
|
+
end
|
821
863
|
end
|
822
864
|
end
|
823
865
|
end
|
@@ -1,11 +1,20 @@
|
|
1
1
|
module Arel
|
2
2
|
module Visitors
|
3
3
|
class WhereSql < Arel::Visitors::ToSql
|
4
|
+
def initialize(inner_visitor, *args, &block)
|
5
|
+
@inner_visitor = inner_visitor
|
6
|
+
super(*args, &block)
|
7
|
+
end
|
8
|
+
|
4
9
|
private
|
5
10
|
|
6
11
|
def visit_Arel_Nodes_SelectCore o, collector
|
7
12
|
collector << "WHERE "
|
8
|
-
|
13
|
+
wheres = o.wheres.map do |where|
|
14
|
+
Nodes::SqlLiteral.new(@inner_visitor.accept(where, collector.class.new).value)
|
15
|
+
end
|
16
|
+
|
17
|
+
inject_join wheres, collector, ' AND '
|
9
18
|
end
|
10
19
|
end
|
11
20
|
end
|