arel_extensions 2.0.4 → 2.0.8
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/.travis.yml +54 -86
- data/README.md +1 -1
- data/Rakefile +13 -2
- data/arel_extensions.gemspec +1 -1
- data/gemfiles/rails4.gemfile +1 -1
- data/gemfiles/rails6.gemfile +1 -1
- data/generate_gems.sh +4 -3
- data/lib/arel_extensions.rb +33 -19
- data/lib/arel_extensions/attributes.rb +0 -1
- data/lib/arel_extensions/boolean_functions.rb +39 -12
- data/lib/arel_extensions/insert_manager.rb +7 -5
- data/lib/arel_extensions/nodes/abs.rb +0 -0
- data/lib/arel_extensions/nodes/case.rb +1 -1
- data/lib/arel_extensions/nodes/ceil.rb +0 -0
- data/lib/arel_extensions/nodes/coalesce.rb +0 -0
- data/lib/arel_extensions/nodes/concat.rb +0 -0
- data/lib/arel_extensions/nodes/duration.rb +0 -0
- data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
- data/lib/arel_extensions/nodes/floor.rb +0 -0
- data/lib/arel_extensions/nodes/function.rb +0 -0
- data/lib/arel_extensions/nodes/is_null.rb +0 -0
- data/lib/arel_extensions/nodes/length.rb +0 -0
- data/lib/arel_extensions/nodes/locate.rb +0 -0
- data/lib/arel_extensions/nodes/rand.rb +0 -0
- data/lib/arel_extensions/nodes/replace.rb +0 -0
- data/lib/arel_extensions/nodes/round.rb +0 -0
- data/lib/arel_extensions/nodes/soundex.rb +0 -0
- data/lib/arel_extensions/nodes/substring.rb +0 -0
- data/lib/arel_extensions/nodes/trim.rb +0 -0
- data/lib/arel_extensions/nodes/union.rb +0 -0
- data/lib/arel_extensions/nodes/wday.rb +0 -0
- data/lib/arel_extensions/predications.rb +22 -19
- data/lib/arel_extensions/set_functions.rb +2 -2
- data/lib/arel_extensions/string_functions.rb +13 -0
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors.rb +1 -1
- data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
- data/lib/arel_extensions/visitors/mysql.rb +24 -9
- data/lib/arel_extensions/visitors/oracle.rb +7 -8
- data/lib/arel_extensions/visitors/postgresql.rb +1 -1
- data/lib/arel_extensions/visitors/sqlite.rb +11 -7
- data/lib/arel_extensions/visitors/to_sql.rb +31 -38
- data/test/arelx_test_helper.rb +28 -0
- data/test/support/fake_record.rb +4 -0
- data/test/test_comparators.rb +8 -7
- data/test/visitors/test_bulk_insert_oracle.rb +4 -3
- data/test/visitors/test_bulk_insert_sqlite.rb +4 -3
- data/test/visitors/test_bulk_insert_to_sql.rb +3 -3
- data/test/visitors/test_oracle.rb +41 -41
- data/test/visitors/test_to_sql.rb +359 -206
- data/test/with_ar/all_agnostic_test.rb +18 -8
- data/test/with_ar/insert_agnostic_test.rb +1 -1
- data/test/with_ar/test_bulk_sqlite.rb +4 -3
- data/test/with_ar/test_math_sqlite.rb +1 -1
- data/test/with_ar/test_string_mysql.rb +1 -1
- data/test/with_ar/test_string_sqlite.rb +1 -1
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +3 -3
- data/test/helper.rb +0 -18
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'arel_extensions/nodes/then'
|
2
3
|
|
3
4
|
module ArelExtensions
|
@@ -8,7 +9,7 @@ module ArelExtensions
|
|
8
9
|
end
|
9
10
|
|
10
11
|
def and *others
|
11
|
-
Arel::Nodes::And.new
|
12
|
+
Arel::Nodes::And.new self, others
|
12
13
|
end
|
13
14
|
|
14
15
|
def ⋁(other)
|
@@ -16,28 +17,54 @@ module ArelExtensions
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def or *others
|
19
|
-
|
20
|
-
if args.length == 1
|
21
|
-
Arel::Nodes::Or.new(self, args.first)
|
22
|
-
else
|
23
|
-
ArelExtensions::Nodes::Or.new([self]+ args)
|
24
|
-
end
|
20
|
+
Arel::Nodes::Or.new self, others
|
25
21
|
end
|
26
22
|
|
27
23
|
def then(t, f = nil)
|
28
24
|
ArelExtensions::Nodes::Then.new [self, t, f]
|
29
25
|
end
|
26
|
+
|
30
27
|
end
|
31
28
|
end
|
32
29
|
|
33
|
-
Arel::Nodes::And
|
30
|
+
class Arel::Nodes::And
|
34
31
|
include ArelExtensions::BooleanFunctions
|
35
|
-
end
|
36
32
|
|
37
|
-
|
38
|
-
|
33
|
+
def self.new *children
|
34
|
+
children =
|
35
|
+
children.flatten.map { |c|
|
36
|
+
c.is_a?(self) ? c.children : c
|
37
|
+
}.flatten
|
38
|
+
super(children)
|
39
|
+
end
|
40
|
+
|
39
41
|
end
|
40
42
|
|
41
|
-
|
43
|
+
# For some reason, Arel's And is properly defined as variadic (it
|
44
|
+
# stores @children, and hashes it all). However Arel's Or is defined
|
45
|
+
# as binary, with only @left and @right, and hashing only @left and @right.
|
46
|
+
#
|
47
|
+
# So reimplement its ctor and accessors.
|
48
|
+
|
49
|
+
class Arel::Nodes::Or
|
42
50
|
include ArelExtensions::BooleanFunctions
|
51
|
+
|
52
|
+
attr_reader :children
|
53
|
+
|
54
|
+
def self.new *children
|
55
|
+
children =
|
56
|
+
children.flatten.map { |c|
|
57
|
+
c.is_a?(self) ? c.children : c
|
58
|
+
}.flatten
|
59
|
+
super(*children)
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize *children
|
63
|
+
@children = children
|
64
|
+
end
|
65
|
+
|
66
|
+
def hash
|
67
|
+
children.hash
|
68
|
+
end
|
69
|
+
|
43
70
|
end
|
@@ -4,21 +4,23 @@ module ArelExtensions
|
|
4
4
|
module InsertManager
|
5
5
|
|
6
6
|
def bulk_insert(cols, data)
|
7
|
+
res_columns = []
|
7
8
|
case cols.first
|
8
9
|
when String, Symbol
|
9
10
|
cols.each { |c|
|
10
|
-
|
11
|
+
res_columns << @ast.relation[c]
|
11
12
|
}
|
12
13
|
when Array
|
13
14
|
if String === cols.first.first
|
14
|
-
|
15
|
+
res_columns = cols.map {|c| [@ast.relation[c.first]] }
|
15
16
|
elsif Arel::Attributes::Attribute == cols.first.first
|
16
|
-
|
17
|
+
res_columns = cols
|
17
18
|
end
|
18
19
|
when NilClass
|
19
|
-
|
20
|
+
res_columns = @ast.relation.columns
|
20
21
|
end
|
21
|
-
self.values = BulkValues.new(
|
22
|
+
self.values = BulkValues.new(res_columns, data)
|
23
|
+
@ast.columns = res_columns
|
22
24
|
end
|
23
25
|
|
24
26
|
class BulkValues < Arel::Nodes::Node
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module ArelExtensions
|
2
2
|
module Predications
|
3
|
+
|
3
4
|
def when right, expression = nil
|
4
5
|
ArelExtensions::Nodes::Case.new(self).when(right,expression)
|
5
6
|
end
|
@@ -20,23 +21,25 @@ module ArelExtensions
|
|
20
21
|
ArelExtensions::Nodes::Cast.new([self,right])
|
21
22
|
end
|
22
23
|
|
23
|
-
def in(other) #In should handle nil element in the Array
|
24
|
+
def in(*other) #In should handle nil element in the Array
|
25
|
+
other = other.first if other.length <= 1
|
24
26
|
case other
|
25
27
|
when Range
|
26
28
|
self.between(other)
|
29
|
+
when Arel::Nodes::Grouping
|
30
|
+
Arel::Nodes::In.new(self, quoted_node(other))
|
27
31
|
when Enumerable
|
28
32
|
nils, values = other.partition{ |v| v.nil? }
|
29
33
|
ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
|
30
|
-
|
31
34
|
# In order of (imagined) decreasing efficiency: nil, values, and then more complex.
|
32
35
|
clauses =
|
33
36
|
nils.uniq.map { |r| self.in(r) } \
|
34
37
|
+ (case values.uniq.size
|
35
38
|
when 0 then []
|
36
|
-
when 1 then [self
|
39
|
+
when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.in(values[0]) : self.eq(values[0])]
|
37
40
|
else [Arel::Nodes::In.new(self, quoted_array(values))] end) \
|
38
41
|
+ ranges.uniq.map { |r| self.in(r) }
|
39
|
-
clauses.empty? ?
|
42
|
+
clauses.empty? ? Arel.false : clauses.reduce(&:or)
|
40
43
|
when nil
|
41
44
|
self.is_null
|
42
45
|
when Arel::SelectManager
|
@@ -46,24 +49,25 @@ module ArelExtensions
|
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
|
-
def not_in(other) #In should handle nil element in the Array
|
52
|
+
def not_in(*other) #In should handle nil element in the Array
|
53
|
+
other = other.first if other.length <= 1
|
50
54
|
case other
|
51
55
|
when Range
|
52
56
|
Arel::Nodes::Not.new(self.between(other))
|
57
|
+
when Arel::Nodes::Grouping
|
58
|
+
Arel::Nodes::NotIn.new(self, quoted_node(other))
|
53
59
|
when Enumerable
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
Arel::Nodes::NotIn.new(self,quoted_array(other))
|
66
|
-
end
|
60
|
+
nils, values = other.partition{ |v| v.nil? }
|
61
|
+
ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
|
62
|
+
# In order of (imagined) decreasing efficiency: nil, values, and then more complex.
|
63
|
+
clauses =
|
64
|
+
nils.uniq.map { |r| self.not_in(r) } \
|
65
|
+
+ (case values.uniq.size
|
66
|
+
when 0 then []
|
67
|
+
when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.not_in(values[0]) : self.not_eq(values[0])]
|
68
|
+
else [Arel::Nodes::NotIn.new(self, quoted_array(values))] end) \
|
69
|
+
+ ranges.uniq.map { |r| self.not_in(r) }
|
70
|
+
Arel::Nodes::And.new clauses
|
67
71
|
when nil
|
68
72
|
self.is_not_null
|
69
73
|
when Arel::SelectManager
|
@@ -93,6 +97,5 @@ module ArelExtensions
|
|
93
97
|
raise(ArgumentError, "#{object.class} can not be converted to CONCAT arg")
|
94
98
|
end
|
95
99
|
end
|
96
|
-
|
97
100
|
end
|
98
101
|
end
|
@@ -23,10 +23,10 @@ module ArelExtensions
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
Arel::Nodes::Union
|
26
|
+
class Arel::Nodes::Union
|
27
27
|
include ArelExtensions::SetFunctions
|
28
28
|
end
|
29
29
|
|
30
|
-
Arel::Nodes::UnionAll
|
30
|
+
class Arel::Nodes::UnionAll
|
31
31
|
include ArelExtensions::SetFunctions
|
32
32
|
end
|
@@ -64,6 +64,19 @@ module ArelExtensions
|
|
64
64
|
grouping_any :imatches, others, escape
|
65
65
|
end
|
66
66
|
|
67
|
+
# def grouping_any method, others, *extra
|
68
|
+
# puts "*******************"
|
69
|
+
# puts method
|
70
|
+
# puts others.inspect
|
71
|
+
# puts extra.inspect
|
72
|
+
# puts "-------------------"
|
73
|
+
# res = super(method,others,*extra)
|
74
|
+
# puts res.to_sql
|
75
|
+
# puts res.inspect
|
76
|
+
# puts "*******************"
|
77
|
+
# res
|
78
|
+
# end
|
79
|
+
|
67
80
|
def imatches_all others, escape = nil
|
68
81
|
grouping_all :imatches, others, escape, escape
|
69
82
|
end
|
@@ -6,7 +6,7 @@ require 'arel_extensions/visitors/postgresql'
|
|
6
6
|
require 'arel_extensions/visitors/sqlite'
|
7
7
|
require 'arel_extensions/visitors/mssql'
|
8
8
|
|
9
|
-
Arel::Visitors::MSSQL
|
9
|
+
class Arel::Visitors::MSSQL
|
10
10
|
include ArelExtensions::Visitors::MSSQL
|
11
11
|
|
12
12
|
alias_method :old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ArelExtensions
|
2
2
|
module Visitors
|
3
|
-
Arel::Visitors::MySQL
|
3
|
+
class Arel::Visitors::MySQL
|
4
4
|
Arel::Visitors::MySQL::DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'WEEK', 'y' => 'YEAR', 'wd' => 'WEEKDAY', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
|
5
5
|
Arel::Visitors::MySQL::DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
|
6
6
|
'%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
|
@@ -193,6 +193,13 @@ module ArelExtensions
|
|
193
193
|
collector
|
194
194
|
end
|
195
195
|
|
196
|
+
def visit_ArelExtensions_Nodes_RegexpReplace o, collector
|
197
|
+
if !regexp_replace_supported?
|
198
|
+
warn("Warning : ArelExtensions: REGEXP_REPLACE does not seem to be available in the current version of the DBMS, it might crash")
|
199
|
+
end
|
200
|
+
super(o,collector)
|
201
|
+
end
|
202
|
+
|
196
203
|
def visit_ArelExtensions_Nodes_Format o, collector
|
197
204
|
case o.col_type
|
198
205
|
when :date, :datetime
|
@@ -403,7 +410,7 @@ module ArelExtensions
|
|
403
410
|
|
404
411
|
def visit_Aggregate_For_AggregateFunction o, collector
|
405
412
|
if !window_supported?
|
406
|
-
warn("Warning : ArelExtensions: Window Functions are not available in the current version
|
413
|
+
warn("Warning : ArelExtensions: Window Functions are not available in the current version of the DBMS.")
|
407
414
|
return collector
|
408
415
|
end
|
409
416
|
|
@@ -449,14 +456,22 @@ module ArelExtensions
|
|
449
456
|
version_supported?('10.2.3', '8.0')
|
450
457
|
end
|
451
458
|
|
452
|
-
def
|
459
|
+
def regexp_replace_supported?
|
460
|
+
version_supported?('10.0.5', '8.0')
|
461
|
+
end
|
462
|
+
|
463
|
+
def version_supported?(mariadb_v = '10.2.3', mysql_v = '5.7.0')
|
453
464
|
conn = Arel::Table.engine.connection
|
454
|
-
conn.send(:mariadb?) &&
|
455
|
-
(conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >=
|
456
|
-
conn.respond_to?(:version) && conn.send(:version) >=
|
457
|
-
|
458
|
-
|
459
|
-
conn.respond_to?(:
|
465
|
+
conn.send(:mariadb?) && \
|
466
|
+
(conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mariadb_v || \
|
467
|
+
conn.respond_to?(:version) && conn.send(:version) >= mariadb_v || \
|
468
|
+
conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mariadb_v) || \
|
469
|
+
!conn.send(:mariadb?) && \
|
470
|
+
(conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mysql_v || \
|
471
|
+
conn.respond_to?(:version) && conn.send(:version) >= mysql_v || \
|
472
|
+
conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mysql_v)
|
473
|
+
# ideally we should parse the instance_variable @full_version because @version contains only the supposedly
|
474
|
+
# corresponding mysql version of the current mariadb version (which is not very helpful most of the time)
|
460
475
|
end
|
461
476
|
|
462
477
|
def visit_ArelExtensions_Nodes_Json o,collector
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#require 'oracle_visitor'
|
2
2
|
module ArelExtensions
|
3
3
|
module Visitors
|
4
|
-
Arel::Visitors::Oracle
|
4
|
+
class Arel::Visitors::Oracle
|
5
5
|
|
6
6
|
SPECIAL_CHARS = {"\t" => 'CHR(9)', "\n" => 'CHR(10)', "\r" => 'CHR(13)'}
|
7
7
|
Arel::Visitors::Oracle::DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'IW', 'y' => 'YEAR', 'wd' => 'D', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
|
@@ -467,14 +467,12 @@ module ArelExtensions
|
|
467
467
|
o.left.each_with_index do |row, idx| # values
|
468
468
|
collector << " UNION ALL " if idx != 0
|
469
469
|
collector << "(SELECT "
|
470
|
-
|
471
|
-
|
472
|
-
v.expressions.each_with_index { |value, i|
|
470
|
+
len = row.length - 1
|
471
|
+
row.zip(o.cols).each_with_index { |(value, attr), i|
|
473
472
|
case value
|
474
473
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
475
474
|
collector = visit value, collector
|
476
475
|
else
|
477
|
-
attr = v.columns[i]
|
478
476
|
collector << quote(value, attr && column_for(attr)).to_s
|
479
477
|
end
|
480
478
|
collector << Arel::Visitors::Oracle::COMMA unless i == len
|
@@ -490,12 +488,13 @@ module ArelExtensions
|
|
490
488
|
o.left.each_with_index do |row, idx|
|
491
489
|
collector << " UNION ALL " if idx != 0
|
492
490
|
collector << "(SELECT "
|
493
|
-
|
494
|
-
|
495
|
-
v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
|
491
|
+
len = row.length - 1
|
492
|
+
row.zip(o.cols).each_with_index { |(value, attr), i|
|
496
493
|
case value
|
497
494
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
498
495
|
collector = visit value, collector
|
496
|
+
when Integer
|
497
|
+
collector << value.to_s
|
499
498
|
else
|
500
499
|
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
501
500
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ArelExtensions
|
2
2
|
module Visitors
|
3
|
-
Arel::Visitors::PostgreSQL
|
3
|
+
class Arel::Visitors::PostgreSQL
|
4
4
|
Arel::Visitors::PostgreSQL::DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'WEEK', 'y' => 'YEAR', 'wd' => 'DOW', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
|
5
5
|
Arel::Visitors::PostgreSQL::DATE_FORMAT_DIRECTIVES = {
|
6
6
|
'%Y' => 'IYYY', '%C' => 'CC', '%y' => 'YY', '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ArelExtensions
|
2
2
|
module Visitors
|
3
|
-
Arel::Visitors::SQLite
|
3
|
+
class Arel::Visitors::SQLite
|
4
4
|
Arel::Visitors::SQLite::DATE_MAPPING = {'d' => '%d', 'm' => '%m', 'w' => '%W', 'y' => '%Y', 'wd' => '%w', 'M' => '%M', 'h' => '%H', 'mn' => '%M', 's' => '%S'}
|
5
5
|
Arel::Visitors::SQLite::DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
|
6
6
|
'%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
|
@@ -235,9 +235,8 @@ module ArelExtensions
|
|
235
235
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
236
236
|
o.left.each_with_index do |row, idx|
|
237
237
|
collector << 'SELECT '
|
238
|
-
|
239
|
-
|
240
|
-
v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
|
238
|
+
len = row.length - 1
|
239
|
+
row.zip(o.cols).each_with_index { |(value, attr), i|
|
241
240
|
case value
|
242
241
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
243
242
|
collector = visit value.as(attr.name), collector
|
@@ -258,12 +257,17 @@ module ArelExtensions
|
|
258
257
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
259
258
|
o.left.each_with_index do |row, idx|
|
260
259
|
collector << 'SELECT '
|
261
|
-
|
262
|
-
|
263
|
-
v.expressions.zip(v.columns).each_with_index { |(value, attr), i|
|
260
|
+
len = row.length - 1
|
261
|
+
row.zip(o.cols).each_with_index { |(value, attr), i|
|
264
262
|
case value
|
265
263
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
266
264
|
collector = visit value.as(attr.name), collector
|
265
|
+
when Integer
|
266
|
+
collector << value.to_s
|
267
|
+
if idx == 0
|
268
|
+
collector << " AS "
|
269
|
+
collector << quote(attr.name)
|
270
|
+
end
|
267
271
|
else
|
268
272
|
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
269
273
|
if idx == 0
|