arel_extensions 1.2.18 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +183 -0
- data/README.md +42 -0
- data/arel_extensions.gemspec +3 -3
- data/gemfiles/rails5_2.gemfile +6 -5
- data/gemfiles/rails6.gemfile +6 -6
- data/gemfiles/rails6_1.gemfile +30 -0
- data/gemfiles/rails7.gemfile +30 -0
- data/gemspecs/arel_extensions-v1.gemspec +28 -0
- data/{gemspec_v2 → gemspecs}/arel_extensions-v2.gemspec +1 -1
- data/generate_gems.sh +4 -3
- data/lib/arel_extensions/aliases.rb +14 -0
- data/lib/arel_extensions/attributes.rb +2 -0
- data/lib/arel_extensions/insert_manager.rb +19 -17
- data/lib/arel_extensions/math.rb +16 -16
- data/lib/arel_extensions/math_functions.rb +7 -3
- data/lib/arel_extensions/nodes/case.rb +2 -1
- data/lib/arel_extensions/nodes/cast.rb +1 -1
- data/lib/arel_extensions/nodes/concat.rb +1 -1
- data/lib/arel_extensions/nodes/function.rb +9 -3
- data/lib/arel_extensions/nodes/json.rb +3 -1
- data/lib/arel_extensions/nodes/length.rb +6 -0
- data/lib/arel_extensions/nodes/replace.rb +0 -8
- data/lib/arel_extensions/nodes/std.rb +4 -4
- data/lib/arel_extensions/nodes/union.rb +1 -1
- data/lib/arel_extensions/nodes/union_all.rb +1 -1
- data/lib/arel_extensions/string_functions.rb +10 -2
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/mssql.rb +1 -1
- data/lib/arel_extensions/visitors/mysql.rb +9 -2
- data/lib/arel_extensions/visitors/oracle.rb +2 -2
- data/lib/arel_extensions/visitors/oracle12.rb +0 -12
- data/lib/arel_extensions/visitors/postgresql.rb +14 -8
- data/lib/arel_extensions/visitors/to_sql.rb +26 -9
- data/lib/arel_extensions/visitors.rb +9 -1
- data/lib/arel_extensions.rb +64 -14
- data/test/database.yml +2 -0
- data/test/real_db_test.rb +5 -1
- data/test/visitors/test_to_sql.rb +18 -11
- data/test/with_ar/all_agnostic_test.rb +20 -4
- data/test/with_ar/insert_agnostic_test.rb +6 -2
- data/test/with_ar/test_bulk_sqlite.rb +6 -2
- data/test/with_ar/test_math_sqlite.rb +6 -2
- data/test/with_ar/test_string_mysql.rb +6 -2
- data/test/with_ar/test_string_sqlite.rb +6 -2
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +14 -9
@@ -21,7 +21,11 @@ module ArelExtensions
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def as other
|
24
|
-
Arel::Nodes::As.new(self, Arel.sql(other))
|
24
|
+
res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
|
25
|
+
if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("9.0.0")
|
26
|
+
self.alias = Arel.sql(other)
|
27
|
+
end
|
28
|
+
res
|
25
29
|
end
|
26
30
|
|
27
31
|
def expr
|
@@ -48,7 +52,9 @@ module ArelExtensions
|
|
48
52
|
case att
|
49
53
|
when Arel::Attributes::Attribute
|
50
54
|
begin
|
51
|
-
Arel::Table.engine.connection.
|
55
|
+
if Arel::Table.engine.connection.tables.include? att.relation.table_name
|
56
|
+
Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
|
57
|
+
end
|
52
58
|
rescue
|
53
59
|
att
|
54
60
|
end
|
@@ -76,7 +82,7 @@ module ArelExtensions
|
|
76
82
|
when ActiveSupport::Duration
|
77
83
|
Arel.sql(object.to_i)
|
78
84
|
when Array
|
79
|
-
Arel
|
85
|
+
Arel.grouping(object.map{|e| convert_to_node(e)})
|
80
86
|
else
|
81
87
|
raise(ArgumentError, "#{object.class} cannot be converted to CONCAT arg")
|
82
88
|
end
|
@@ -10,10 +10,6 @@ module ArelExtensions
|
|
10
10
|
@substitute = convert_to_node(substitute)
|
11
11
|
super([@left,@pattern,@substitute])
|
12
12
|
end
|
13
|
-
|
14
|
-
def +(other)
|
15
|
-
return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
|
16
|
-
end
|
17
13
|
end
|
18
14
|
|
19
15
|
class RegexpReplace < Function
|
@@ -26,10 +22,6 @@ module ArelExtensions
|
|
26
22
|
@substitute = convert_to_node(substitute)
|
27
23
|
super([@left,@pattern,@substitute])
|
28
24
|
end
|
29
|
-
|
30
|
-
def +(other)
|
31
|
-
return ArelExtensions::Nodes::Concat.new(self.expressions + [other])
|
32
|
-
end
|
33
25
|
end
|
34
26
|
end
|
35
27
|
end
|
@@ -4,9 +4,9 @@ module ArelExtensions
|
|
4
4
|
RETURN_TYPE = :number
|
5
5
|
attr_accessor :unbiased_estimator
|
6
6
|
|
7
|
-
def initialize node, opts
|
7
|
+
def initialize node, **opts
|
8
8
|
@unbiased_estimator = opts[:unbiased] ? true : false
|
9
|
-
super node, opts
|
9
|
+
super node, **opts
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -14,9 +14,9 @@ module ArelExtensions
|
|
14
14
|
RETURN_TYPE = :number
|
15
15
|
attr_accessor :unbiased_estimator
|
16
16
|
|
17
|
-
def initialize node, opts
|
17
|
+
def initialize node, **opts
|
18
18
|
@unbiased_estimator = opts[:unbiased] ? true : false
|
19
|
-
super node, opts
|
19
|
+
super node, **opts
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -14,7 +14,7 @@ module ArelExtensions
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def as other
|
17
|
-
Arel::Nodes::TableAlias.new Arel
|
17
|
+
Arel::Nodes::TableAlias.new Arel.grouping(self), Arel::Nodes::SqlLiteral.new(other.to_s)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -10,7 +10,7 @@ module ArelExtensions
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def as other
|
13
|
-
Arel::Nodes::TableAlias.new Arel
|
13
|
+
Arel::Nodes::TableAlias.new Arel.grouping(self), Arel::Nodes::SqlLiteral.new(other.to_s)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -24,9 +24,17 @@ module ArelExtensions
|
|
24
24
|
ArelExtensions::Nodes::FindInSet.new [other, self]
|
25
25
|
end
|
26
26
|
|
27
|
-
# LENGTH function returns the length of the value in a text field.
|
27
|
+
# LENGTH function returns the length (bytewise) of the value in a text field.
|
28
28
|
def length
|
29
|
-
ArelExtensions::Nodes::Length.new
|
29
|
+
ArelExtensions::Nodes::Length.new self, true
|
30
|
+
end
|
31
|
+
|
32
|
+
def byte_length
|
33
|
+
ArelExtensions::Nodes::Length.new self, true
|
34
|
+
end
|
35
|
+
|
36
|
+
def char_length
|
37
|
+
ArelExtensions::Nodes::Length.new self, false
|
30
38
|
end
|
31
39
|
|
32
40
|
# LOCATE function returns the first starting position of a string in another string.
|
@@ -354,9 +354,16 @@ module ArelExtensions
|
|
354
354
|
else
|
355
355
|
collector = visit o.left, collector
|
356
356
|
end
|
357
|
-
collector << " AS
|
357
|
+
collector << " AS "
|
358
|
+
|
359
|
+
# sometimes these values are already quoted, if they are, don't double quote it
|
360
|
+
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '`' && o.right[-1] != '`'
|
361
|
+
|
362
|
+
collector << '`' if quote
|
358
363
|
collector = visit o.right, collector
|
359
|
-
collector <<
|
364
|
+
collector << '`' if quote
|
365
|
+
|
366
|
+
collector
|
360
367
|
collector
|
361
368
|
end
|
362
369
|
|
@@ -5,7 +5,7 @@ module ArelExtensions
|
|
5
5
|
SPECIAL_CHARS = {"\t" => 'CHR(9)', "\n" => 'CHR(10)', "\r" => 'CHR(13)'}
|
6
6
|
DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'IW', 'y' => 'YEAR', 'wd' => 'D', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
|
7
7
|
DATE_FORMAT_DIRECTIVES = {
|
8
|
-
'%Y' => '
|
8
|
+
'%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY', '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
|
9
9
|
'%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
|
10
10
|
'%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
|
11
11
|
'%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz' # seconds, subseconds
|
@@ -308,7 +308,7 @@ module ArelExtensions
|
|
308
308
|
end
|
309
309
|
|
310
310
|
def visit_ArelExtensions_Nodes_Length o, collector
|
311
|
-
collector << "LENGTH("
|
311
|
+
collector << "LENGTH#{o.bytewise ? 'B' : ''}("
|
312
312
|
collector = visit o.expr, collector
|
313
313
|
collector << ")"
|
314
314
|
collector
|
@@ -2,18 +2,6 @@ module ArelExtensions
|
|
2
2
|
module Visitors
|
3
3
|
Arel::Visitors.send(:remove_const,'Oracle12') if Arel::Visitors.const_defined?('Oracle12')
|
4
4
|
Arel::Visitors.const_set('Oracle12',Class.new(Arel::Visitors::Oracle)).class_eval do
|
5
|
-
def visit_Arel_Nodes_SelectStatement(o, collector)
|
6
|
-
# Oracle does not allow LIMIT clause with select for update
|
7
|
-
if o.limit && o.lock
|
8
|
-
raise ArgumentError, <<-MSG
|
9
|
-
'Combination of limit and lock is not supported.
|
10
|
-
because generated SQL statements
|
11
|
-
`SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.`
|
12
|
-
MSG
|
13
|
-
end
|
14
|
-
|
15
|
-
super
|
16
|
-
end
|
17
5
|
|
18
6
|
def visit_Arel_Nodes_SelectOptions(o, collector)
|
19
7
|
collector = maybe_visit o.offset, collector
|
@@ -7,7 +7,7 @@ module ArelExtensions
|
|
7
7
|
}.freeze
|
8
8
|
|
9
9
|
DATE_FORMAT_DIRECTIVES = {
|
10
|
-
'%Y' => '
|
10
|
+
'%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY',
|
11
11
|
'%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
|
12
12
|
'%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
|
13
13
|
'%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
|
@@ -86,9 +86,15 @@ module ArelExtensions
|
|
86
86
|
else
|
87
87
|
collector = visit o.left, collector
|
88
88
|
end
|
89
|
-
collector << " AS
|
89
|
+
collector << " AS "
|
90
|
+
|
91
|
+
# sometimes these values are already quoted, if they are, don't double quote it
|
92
|
+
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
|
93
|
+
|
94
|
+
collector << '"' if quote
|
90
95
|
collector = visit o.right, collector
|
91
|
-
collector << "
|
96
|
+
collector << '"' if quote
|
97
|
+
|
92
98
|
collector
|
93
99
|
end
|
94
100
|
|
@@ -373,6 +379,8 @@ module ArelExtensions
|
|
373
379
|
Arel::Nodes::SqlLiteral.new('date')
|
374
380
|
when :binary
|
375
381
|
Arel::Nodes::SqlLiteral.new('binary')
|
382
|
+
when :jsonb
|
383
|
+
Arel::Nodes::SqlLiteral.new('jsonb')
|
376
384
|
else
|
377
385
|
Arel::Nodes::SqlLiteral.new(o.as_attr.to_s)
|
378
386
|
end
|
@@ -401,13 +409,13 @@ module ArelExtensions
|
|
401
409
|
ArelExtensions::Nodes::Concat.new([
|
402
410
|
Arel::Nodes::NamedFunction.new('TRIM',[
|
403
411
|
Arel::Nodes::NamedFunction.new('TO_CHAR',[
|
404
|
-
col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
|
412
|
+
Arel.when(col.not_eq 0).then(col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)).else(1),
|
405
413
|
Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
|
406
414
|
])]),
|
407
415
|
o.type,
|
408
416
|
Arel::Nodes::NamedFunction.new('TRIM',[
|
409
417
|
Arel::Nodes::NamedFunction.new('TO_CHAR',[
|
410
|
-
col.abs.log10.floor,
|
418
|
+
Arel.when(col.not_eq 0).then(col.abs.log10.floor).else(0),
|
411
419
|
Arel::Nodes.build_quoted('FM'+nines_before)
|
412
420
|
])])
|
413
421
|
])
|
@@ -511,8 +519,6 @@ module ArelExtensions
|
|
511
519
|
collector << '::jsonb'
|
512
520
|
when NilClass
|
513
521
|
collector << %Q['null'::jsonb]
|
514
|
-
when Arel::Attributes::Attribute
|
515
|
-
collector = visit o.dict.cast(:jsonb), collector
|
516
522
|
else
|
517
523
|
collector = visit o.dict, collector
|
518
524
|
collector << '::jsonb'
|
@@ -532,7 +538,7 @@ module ArelExtensions
|
|
532
538
|
|
533
539
|
def visit_ArelExtensions_Nodes_JsonGet o,collector
|
534
540
|
collector = visit o.dict, collector
|
535
|
-
collector << '
|
541
|
+
collector << ' ->> '
|
536
542
|
collector = visit o.key, collector
|
537
543
|
collector
|
538
544
|
end
|
@@ -3,6 +3,20 @@ module ArelExtensions
|
|
3
3
|
class Arel::Visitors::ToSql
|
4
4
|
COMMA = ', ' unless defined?(COMMA)
|
5
5
|
|
6
|
+
# Escape properly the string expression expr.
|
7
|
+
# Take care of escaping.
|
8
|
+
def make_json_string expr
|
9
|
+
Arel::Nodes.build_quoted('"') \
|
10
|
+
+ expr
|
11
|
+
.coalesce('')
|
12
|
+
.replace('\\','\\\\').replace('"','\"').replace("\n", '\n') \
|
13
|
+
+ '"'
|
14
|
+
end
|
15
|
+
|
16
|
+
def make_json_null
|
17
|
+
Arel::Nodes.build_quoted("null")
|
18
|
+
end
|
19
|
+
|
6
20
|
# Math Functions
|
7
21
|
def visit_ArelExtensions_Nodes_Abs o, collector
|
8
22
|
collector << "ABS("
|
@@ -108,7 +122,7 @@ module ArelExtensions
|
|
108
122
|
end
|
109
123
|
|
110
124
|
def visit_ArelExtensions_Nodes_Length o, collector
|
111
|
-
collector << "LENGTH("
|
125
|
+
collector << "#{o.bytewise ? '' : 'CHAR_'}LENGTH("
|
112
126
|
collector = visit o.left, collector
|
113
127
|
collector << ")"
|
114
128
|
collector
|
@@ -339,6 +353,9 @@ module ArelExtensions
|
|
339
353
|
# override
|
340
354
|
remove_method(:visit_Arel_Nodes_As) rescue nil # if Arel::Visitors::ToSql.method_defined?(:visit_Arel_Nodes_As)
|
341
355
|
def visit_Arel_Nodes_As o, collector
|
356
|
+
if o.left.respond_to?(:alias)
|
357
|
+
o.left.alias = nil
|
358
|
+
end
|
342
359
|
if o.left.is_a?(Arel::Nodes::Binary)
|
343
360
|
collector << '('
|
344
361
|
collector = visit o.left, collector
|
@@ -589,20 +606,20 @@ module ArelExtensions
|
|
589
606
|
def json_value(o,v)
|
590
607
|
case o.type_of_node(v)
|
591
608
|
when :string
|
592
|
-
Arel.when(v.is_null).then(
|
609
|
+
Arel.when(v.is_null).then(make_json_null).else(make_json_string(v))
|
593
610
|
when :date
|
594
611
|
s = v.format('%Y-%m-%d')
|
595
|
-
Arel.when(s.is_null).then(
|
612
|
+
Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
|
596
613
|
when :datetime
|
597
614
|
s = v.format('%Y-%m-%dT%H:%M:%S')
|
598
|
-
Arel.when(s.is_null).then(
|
615
|
+
Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
|
599
616
|
when :time
|
600
617
|
s = v.format('%H:%M:%S')
|
601
|
-
Arel.when(s.is_null).then(
|
618
|
+
Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
|
602
619
|
when :nil
|
603
|
-
|
620
|
+
make_json_null
|
604
621
|
else
|
605
|
-
ArelExtensions::Nodes::Cast.new([v, :string]).coalesce(
|
622
|
+
ArelExtensions::Nodes::Cast.new([v, :string]).coalesce(make_json_null)
|
606
623
|
end
|
607
624
|
end
|
608
625
|
|
@@ -624,7 +641,7 @@ module ArelExtensions
|
|
624
641
|
if i != 0
|
625
642
|
res += ', '
|
626
643
|
end
|
627
|
-
res +=
|
644
|
+
res += make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
|
628
645
|
res += json_value(o,v)
|
629
646
|
end
|
630
647
|
res += '}'
|
@@ -646,7 +663,7 @@ module ArelExtensions
|
|
646
663
|
if i != 0
|
647
664
|
res = res + ', '
|
648
665
|
end
|
649
|
-
kv =
|
666
|
+
kv = make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
|
650
667
|
kv += json_value(o,v)
|
651
668
|
res = res + kv.group_concat(', ', order: Array(orders)).coalesce('')
|
652
669
|
end
|
@@ -9,9 +9,17 @@ if defined?(Arel::Visitors::Oracle)
|
|
9
9
|
require 'arel_extensions/visitors/oracle12'
|
10
10
|
end
|
11
11
|
|
12
|
-
if defined?(Arel::Visitors::MSSQL)
|
12
|
+
if defined?(Arel::Visitors::SQLServer) || defined?(Arel::Visitors::MSSQL)
|
13
13
|
require 'arel_extensions/visitors/mssql'
|
14
|
+
end
|
14
15
|
|
16
|
+
if defined?(Arel::Visitors::SQLServer)
|
17
|
+
class Arel::Visitors::SQLServer
|
18
|
+
include ArelExtensions::Visitors::MSSQL
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if defined?(Arel::Visitors::MSSQL)
|
15
23
|
class Arel::Visitors::MSSQL
|
16
24
|
include ArelExtensions::Visitors::MSSQL
|
17
25
|
|
data/lib/arel_extensions.rb
CHANGED
@@ -16,8 +16,10 @@ class Arel::Nodes::Casted
|
|
16
16
|
include Arel::AliasPredication
|
17
17
|
|
18
18
|
# They forget to define hash.
|
19
|
-
|
20
|
-
|
19
|
+
if Gem::Version.new(Arel::VERSION) < Gem::Version.new("10.0.0")
|
20
|
+
def hash
|
21
|
+
[self.class, self.val, self.attribute].hash
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
@@ -31,6 +33,12 @@ class Arel::Nodes::Grouping
|
|
31
33
|
include Arel::OrderPredications
|
32
34
|
end
|
33
35
|
|
36
|
+
class Arel::Nodes::Ordering
|
37
|
+
def eql? other
|
38
|
+
self.hash.eql? other.hash
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
34
42
|
class Arel::Nodes::Function
|
35
43
|
include Arel::Math
|
36
44
|
include Arel::Expressions
|
@@ -44,6 +52,7 @@ if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("7.1.0")
|
|
44
52
|
end
|
45
53
|
|
46
54
|
require 'arel_extensions/version'
|
55
|
+
require 'arel_extensions/aliases'
|
47
56
|
require 'arel_extensions/attributes'
|
48
57
|
require 'arel_extensions/visitors'
|
49
58
|
require 'arel_extensions/nodes'
|
@@ -80,11 +89,13 @@ module Arel
|
|
80
89
|
end
|
81
90
|
|
82
91
|
def self.json *expr
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
92
|
+
ArelExtensions::Nodes::Json.new(
|
93
|
+
if expr.length == 1
|
94
|
+
expr.first
|
95
|
+
else
|
96
|
+
expr
|
97
|
+
end
|
98
|
+
)
|
88
99
|
end
|
89
100
|
|
90
101
|
def self.when condition
|
@@ -92,20 +103,31 @@ module Arel
|
|
92
103
|
end
|
93
104
|
|
94
105
|
def self.duration s, expr
|
95
|
-
ArelExtensions::Nodes::Duration.new(s
|
106
|
+
ArelExtensions::Nodes::Duration.new("#{s}i", expr)
|
96
107
|
end
|
97
108
|
|
109
|
+
def self.grouping *v
|
110
|
+
Arel::Nodes::Grouping.new(*v)
|
111
|
+
end
|
112
|
+
|
113
|
+
# The TRUE pseudo literal.
|
98
114
|
def self.true
|
99
|
-
Arel::Nodes::Equality.new(1,1)
|
115
|
+
Arel::Nodes::Equality.new(1, 1)
|
100
116
|
end
|
101
117
|
|
118
|
+
# The FALSE pseudo literal.
|
102
119
|
def self.false
|
103
|
-
Arel::Nodes::Equality.new(1,0)
|
120
|
+
Arel::Nodes::Equality.new(1, 0)
|
121
|
+
end
|
122
|
+
|
123
|
+
# The NULL literal.
|
124
|
+
def self.null
|
125
|
+
Arel::Nodes.build_quoted(nil)
|
104
126
|
end
|
105
127
|
|
106
128
|
def self.tuple *v
|
107
|
-
tmp = Arel
|
108
|
-
Arel
|
129
|
+
tmp = Arel.grouping(nil)
|
130
|
+
Arel.grouping v.map{|e| tmp.convert_to_node(e)}
|
109
131
|
end
|
110
132
|
end
|
111
133
|
|
@@ -115,6 +137,7 @@ class Arel::Attributes::Attribute
|
|
115
137
|
end
|
116
138
|
|
117
139
|
class Arel::Nodes::Function
|
140
|
+
include ArelExtensions::Aliases
|
118
141
|
include ArelExtensions::Math
|
119
142
|
include ArelExtensions::Comparators
|
120
143
|
include ArelExtensions::DateDuration
|
@@ -126,7 +149,11 @@ class Arel::Nodes::Function
|
|
126
149
|
|
127
150
|
alias_method(:old_as, :as) rescue nil
|
128
151
|
def as other
|
129
|
-
Arel::Nodes::As.new(self, Arel.sql(other))
|
152
|
+
res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
|
153
|
+
if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("9.0.0")
|
154
|
+
self.alias = Arel.sql(other)
|
155
|
+
end
|
156
|
+
res
|
130
157
|
end
|
131
158
|
end
|
132
159
|
|
@@ -146,6 +173,9 @@ class Arel::Nodes::Unary
|
|
146
173
|
include ArelExtensions::MathFunctions
|
147
174
|
include ArelExtensions::Comparators
|
148
175
|
include ArelExtensions::Predications
|
176
|
+
def eql? other
|
177
|
+
hash == other.hash
|
178
|
+
end
|
149
179
|
end
|
150
180
|
|
151
181
|
class Arel::Nodes::Binary
|
@@ -155,6 +185,9 @@ class Arel::Nodes::Binary
|
|
155
185
|
include ArelExtensions::Comparators
|
156
186
|
include ArelExtensions::BooleanFunctions
|
157
187
|
include ArelExtensions::Predications
|
188
|
+
def eql? other
|
189
|
+
hash == other.hash
|
190
|
+
end
|
158
191
|
end
|
159
192
|
|
160
193
|
class Arel::Nodes::Equality
|
@@ -171,6 +204,19 @@ end
|
|
171
204
|
class Arel::SelectManager
|
172
205
|
include ArelExtensions::SetFunctions
|
173
206
|
include ArelExtensions::Nodes
|
207
|
+
|
208
|
+
def as table_name
|
209
|
+
Arel::Nodes::TableAlias.new(self, table_name)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Install an alias, if present.
|
213
|
+
def xas table_name
|
214
|
+
if table_name.present?
|
215
|
+
as table_name
|
216
|
+
else
|
217
|
+
self
|
218
|
+
end
|
219
|
+
end
|
174
220
|
end
|
175
221
|
|
176
222
|
class Arel::Nodes::As
|
@@ -180,7 +226,11 @@ end
|
|
180
226
|
class Arel::Table
|
181
227
|
alias_method(:old_alias, :alias) rescue nil
|
182
228
|
def alias(name = "#{self.name}_2")
|
183
|
-
name.
|
229
|
+
if name.present?
|
230
|
+
Arel::Nodes::TableAlias.new(self, name)
|
231
|
+
else
|
232
|
+
self
|
233
|
+
end
|
184
234
|
end
|
185
235
|
end
|
186
236
|
|
data/test/database.yml
CHANGED
data/test/real_db_test.rb
CHANGED
@@ -8,7 +8,11 @@ require 'arel_extensions'
|
|
8
8
|
def setup_db
|
9
9
|
ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
|
10
10
|
ActiveRecord::Base.establish_connection(ENV['DB'].try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
|
11
|
-
ActiveRecord::
|
11
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
12
|
+
ActiveRecord.default_timezone = :utc
|
13
|
+
else
|
14
|
+
ActiveRecord::Base.default_timezone = :utc
|
15
|
+
end
|
12
16
|
@cnx = ActiveRecord::Base.connection
|
13
17
|
if ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
|
14
18
|
$sqlite = true
|
@@ -282,16 +282,23 @@ module ArelExtensions
|
|
282
282
|
.must_be_like %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END ILIKE 'value'}
|
283
283
|
end
|
284
284
|
|
285
|
-
it "should be possible to use as on anything" do
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
.
|
285
|
+
it "should be possible to use as/xas on anything" do
|
286
|
+
{
|
287
|
+
@table[:name] => %{"users"."name" AS alias},
|
288
|
+
@table[:name].concat(' test') => %{CONCAT("users"."name", ' test') AS alias},
|
289
|
+
(@table[:name] + ' test') => %{CONCAT("users"."name", ' test') AS alias},
|
290
|
+
(@table[:age] + 42) => %{("users"."age" + 42) AS alias},
|
291
|
+
@table[:name].coalesce('') => %{COALESCE("users"."name", '') AS alias},
|
292
|
+
Arel::Nodes.build_quoted('test') => %{'test' AS alias},
|
293
|
+
@table.project(@table[:name]) => %{(SELECT "users"."name" FROM "users") "alias"},
|
294
|
+
@table[:name].when("smith").then("cool").else("uncool") => %{CASE "users"."name" WHEN 'smith' THEN 'cool' ELSE 'uncool' END AS alias},
|
295
|
+
}.each do |exp, res|
|
296
|
+
_(compile(exp.as('alias'))).must_be_like res
|
297
|
+
_(compile(exp.xas('alias'))).must_be_like res
|
298
|
+
|
299
|
+
res_no_alias = res.gsub(/\s*(?:AS alias|"alias")\s*\z/, '')
|
300
|
+
_(compile(exp.xas(nil))).must_be_like res_no_alias
|
301
|
+
end
|
295
302
|
end
|
296
303
|
|
297
304
|
it "should accept comparators on functions" do
|
@@ -373,7 +380,7 @@ module ArelExtensions
|
|
373
380
|
end
|
374
381
|
|
375
382
|
it "should respecting Grouping" do
|
376
|
-
g = ->(*v) { Arel
|
383
|
+
g = ->(*v) { Arel.grouping(v) }
|
377
384
|
_(compile(g[@table[:id], @table[:age]].in [g[1, 42]]))
|
378
385
|
.must_be_like %{("users"."id", "users"."age") IN ((1, 42))}
|
379
386
|
_(compile(g[@table[:id], @table[:age]].in [g[1, 42], g[2, 51]]))
|