arel_extensions 1.2.3 → 1.2.14

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 (91) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +7 -4
  3. data/.travis.yml +59 -91
  4. data/Gemfile +2 -2
  5. data/README.md +17 -12
  6. data/Rakefile +38 -27
  7. data/appveyor.yml +1 -1
  8. data/arel_extensions.gemspec +1 -1
  9. data/functions.html +3 -3
  10. data/gemfiles/rails4.gemfile +1 -1
  11. data/gemfiles/rails6.gemfile +30 -0
  12. data/gemspec_v2/arel_extensions-v2.gemspec +28 -0
  13. data/generate_gems.sh +14 -0
  14. data/init/mssql.sql +4 -4
  15. data/init/mysql.sql +38 -38
  16. data/init/postgresql.sql +21 -21
  17. data/lib/arel_extensions.rb +63 -19
  18. data/lib/arel_extensions/attributes.rb +0 -1
  19. data/lib/arel_extensions/boolean_functions.rb +38 -13
  20. data/lib/arel_extensions/common_sql_functions.rb +5 -4
  21. data/lib/arel_extensions/comparators.rb +4 -2
  22. data/lib/arel_extensions/insert_manager.rb +26 -24
  23. data/lib/arel_extensions/math.rb +3 -3
  24. data/lib/arel_extensions/math_functions.rb +10 -5
  25. data/lib/arel_extensions/nodes.rb +1 -1
  26. data/lib/arel_extensions/nodes/abs.rb +0 -0
  27. data/lib/arel_extensions/nodes/aggregate_function.rb +14 -0
  28. data/lib/arel_extensions/nodes/case.rb +8 -4
  29. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  30. data/lib/arel_extensions/nodes/coalesce.rb +2 -2
  31. data/lib/arel_extensions/nodes/collate.rb +1 -1
  32. data/lib/arel_extensions/nodes/concat.rb +6 -13
  33. data/lib/arel_extensions/nodes/date_diff.rb +3 -5
  34. data/lib/arel_extensions/nodes/duration.rb +0 -2
  35. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  36. data/lib/arel_extensions/nodes/floor.rb +0 -0
  37. data/lib/arel_extensions/nodes/format.rb +8 -8
  38. data/lib/arel_extensions/nodes/formatted_number.rb +23 -23
  39. data/lib/arel_extensions/nodes/function.rb +2 -0
  40. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  41. data/lib/arel_extensions/nodes/json.rb +28 -30
  42. data/lib/arel_extensions/nodes/length.rb +0 -0
  43. data/lib/arel_extensions/nodes/locate.rb +0 -0
  44. data/lib/arel_extensions/nodes/matches.rb +4 -4
  45. data/lib/arel_extensions/nodes/power.rb +6 -5
  46. data/lib/arel_extensions/nodes/rand.rb +0 -0
  47. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  48. data/lib/arel_extensions/nodes/replace.rb +24 -6
  49. data/lib/arel_extensions/nodes/round.rb +5 -5
  50. data/lib/arel_extensions/nodes/soundex.rb +16 -15
  51. data/lib/arel_extensions/nodes/std.rb +19 -21
  52. data/lib/arel_extensions/nodes/substring.rb +8 -15
  53. data/lib/arel_extensions/nodes/sum.rb +7 -0
  54. data/lib/arel_extensions/nodes/trim.rb +3 -3
  55. data/lib/arel_extensions/nodes/union.rb +2 -3
  56. data/lib/arel_extensions/nodes/union_all.rb +0 -1
  57. data/lib/arel_extensions/nodes/wday.rb +0 -0
  58. data/lib/arel_extensions/null_functions.rb +2 -2
  59. data/lib/arel_extensions/predications.rb +35 -33
  60. data/lib/arel_extensions/set_functions.rb +2 -2
  61. data/lib/arel_extensions/string_functions.rb +34 -12
  62. data/lib/arel_extensions/tasks.rb +5 -5
  63. data/lib/arel_extensions/version.rb +1 -1
  64. data/lib/arel_extensions/visitors.rb +1 -1
  65. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  66. data/lib/arel_extensions/visitors/mssql.rb +14 -13
  67. data/lib/arel_extensions/visitors/mysql.rb +90 -36
  68. data/lib/arel_extensions/visitors/oracle.rb +16 -16
  69. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  70. data/lib/arel_extensions/visitors/postgresql.rb +78 -32
  71. data/lib/arel_extensions/visitors/sqlite.rb +52 -44
  72. data/lib/arel_extensions/visitors/to_sql.rb +71 -58
  73. data/test/arelx_test_helper.rb +28 -0
  74. data/test/real_db_test.rb +1 -1
  75. data/test/support/fake_record.rb +4 -0
  76. data/test/test_comparators.rb +9 -8
  77. data/test/visitors/test_bulk_insert_oracle.rb +8 -7
  78. data/test/visitors/test_bulk_insert_sqlite.rb +9 -8
  79. data/test/visitors/test_bulk_insert_to_sql.rb +8 -10
  80. data/test/visitors/test_oracle.rb +41 -40
  81. data/test/visitors/test_to_sql.rb +367 -193
  82. data/test/with_ar/all_agnostic_test.rb +68 -35
  83. data/test/with_ar/insert_agnostic_test.rb +3 -2
  84. data/test/with_ar/test_bulk_sqlite.rb +6 -5
  85. data/test/with_ar/test_math_sqlite.rb +4 -4
  86. data/test/with_ar/test_string_mysql.rb +4 -6
  87. data/test/with_ar/test_string_sqlite.rb +3 -7
  88. data/version_v1.rb +3 -0
  89. data/version_v2.rb +3 -0
  90. metadata +13 -7
  91. data/test/helper.rb +0 -18
@@ -8,12 +8,12 @@ module ArelExtensions
8
8
  def is_null
9
9
  ArelExtensions::Nodes::IsNull.new [self]
10
10
  end
11
-
11
+
12
12
  #ISNOTNULL function lets you return an alternative value when an expression is NOT NULL.
13
13
  def is_not_null
14
14
  ArelExtensions::Nodes::IsNotNull.new [self]
15
15
  end
16
-
16
+
17
17
  # returns the first non-null expr in the expression list. You must specify at least two expressions.
18
18
  #If all occurrences of expr evaluate to null, then the function returns null.
19
19
  def coalesce *args
@@ -1,10 +1,11 @@
1
1
  module ArelExtensions
2
2
  module Predications
3
- def when right, expression=nil
3
+
4
+ def when right, expression = nil
4
5
  ArelExtensions::Nodes::Case.new(self).when(right,expression)
5
6
  end
6
7
 
7
- def matches(other, escape=nil,case_sensitive= nil)
8
+ def matches(other, escape = nil,case_sensitive = nil)
8
9
  if Arel::VERSION.to_i < 7
9
10
  Arel::Nodes::Matches.new(self, Arel::Nodes.build_quoted(other), escape)
10
11
  else
@@ -12,7 +13,7 @@ module ArelExtensions
12
13
  end
13
14
  end
14
15
 
15
- def imatches(other, escape=nil)
16
+ def imatches(other, escape = nil)
16
17
  ArelExtensions::Nodes::IMatches.new(self, other, escape)
17
18
  end
18
19
 
@@ -20,51 +21,53 @@ 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
- if other.include?(nil)
29
- other.delete(nil)
30
- case other.length
31
- when 0
32
- self.is_null
33
- when 1
34
- self.is_null.or(self==other[0])
35
- else
36
- self.is_null.or(Arel::Nodes::In.new(self,quoted_array(other)))
37
- end
38
- else
39
- Arel::Nodes::In.new(self,quoted_array(other))
40
- end
32
+ nils, values = other.partition{ |v| v.nil? }
33
+ ranges, values = values.partition{ |v| v.is_a?(Range) || v.is_a?(Arel::SelectManager)}
34
+ # In order of (imagined) decreasing efficiency: nil, values, and then more complex.
35
+ clauses =
36
+ nils.uniq.map { |r| self.in(r) } \
37
+ + (case values.uniq.size
38
+ when 0 then []
39
+ when 1 then [values[0].is_a?(Arel::Nodes::Grouping) ? self.in(values[0]) : self.eq(values[0])]
40
+ else [Arel::Nodes::In.new(self, quoted_array(values))] end) \
41
+ + ranges.uniq.map { |r| self.in(r) }
42
+ clauses.empty? ? Arel.false : clauses.reduce(&:or)
41
43
  when nil
42
44
  self.is_null
43
45
  when Arel::SelectManager
44
46
  Arel::Nodes::In.new(self, other.ast)
45
47
  else
46
- Arel::Nodes::In.new(self,quoted_node(other))
48
+ Arel::Nodes::In.new(self, quoted_node(other))
47
49
  end
48
50
  end
49
51
 
50
- 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
51
54
  case other
52
55
  when Range
53
56
  Arel::Nodes::Not.new(self.between(other))
57
+ when Arel::Nodes::Grouping
58
+ Arel::Nodes::NotIn.new(self, quoted_node(other))
54
59
  when Enumerable
55
- if other.include?(nil)
56
- other.delete(nil)
57
- case other.length
58
- when 0
59
- self.is_not_null
60
- when 1
61
- self.is_not_null.and(self!=other[0])
62
- else
63
- self.is_not_null.and(Arel::Nodes::NotIn.new(self,quoted_array(other)))
64
- end
65
- else
66
- Arel::Nodes::NotIn.new(self,quoted_array(other))
67
- 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
68
71
  when nil
69
72
  self.is_not_null
70
73
  when Arel::SelectManager
@@ -94,6 +97,5 @@ module ArelExtensions
94
97
  raise(ArgumentError, "#{object.class} can not be converted to CONCAT arg")
95
98
  end
96
99
  end
97
-
98
100
  end
99
101
  end
@@ -23,10 +23,10 @@ module ArelExtensions
23
23
  end
24
24
  end
25
25
 
26
- Arel::Nodes::Union.class_eval do
26
+ class Arel::Nodes::Union
27
27
  include ArelExtensions::SetFunctions
28
28
  end
29
29
 
30
- Arel::Nodes::UnionAll.class_eval do
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
@@ -92,7 +105,7 @@ module ArelExtensions
92
105
  ArelExtensions::Nodes::SMatches.new(self,other)
93
106
  end
94
107
 
95
- def ai_collate
108
+ def ai_collate
96
109
  ArelExtensions::Nodes::Collate.new(self,nil,true,false)
97
110
  end
98
111
 
@@ -100,31 +113,40 @@ module ArelExtensions
100
113
  ArelExtensions::Nodes::Collate.new(self,nil,false,true)
101
114
  end
102
115
 
103
- def collate ai=false,ci=false, option=nil
116
+ def collate ai = false,ci = false, option = nil
104
117
  ArelExtensions::Nodes::Collate.new(self,option,ai,ci)
105
118
  end
106
119
 
107
120
  #REPLACE function replaces a sequence of characters in a string with another set of characters, not case-sensitive.
108
- def replace left, right
109
- ArelExtensions::Nodes::Replace.new [self, left, right]
121
+ def replace pattern, substitute
122
+ if pattern.is_a? Regexp
123
+ ArelExtensions::Nodes::RegexpReplace.new self, pattern, substitute
124
+ else
125
+ ArelExtensions::Nodes::Replace.new self, pattern, substitute
126
+ end
127
+ end
128
+
129
+ def regexp_replace pattern, substitute
130
+ ArelExtensions::Nodes::RegexpReplace.new self, pattern, substitute
110
131
  end
111
-
132
+
112
133
  def concat other
113
134
  ArelExtensions::Nodes::Concat.new [self, other]
114
135
  end
115
136
 
116
137
  #concat elements of a group, separated by sep and ordered by a list of Ascending or Descending
117
- def group_concat sep = nil, *orders
138
+ def group_concat(sep = nil, *orders, group: nil, order: nil)
139
+ if orders.present?
140
+ warn("Warning : ArelExtensions: group_concat: you should now use the kwarg 'order' to specify an order in the group_concat.")
141
+ end
118
142
  order_tabs = [orders].flatten.map{ |o|
119
143
  if o.is_a?(Arel::Nodes::Ascending) || o.is_a?(Arel::Nodes::Descending)
120
144
  o
121
145
  elsif o.respond_to?(:asc)
122
146
  o.asc
123
- else
124
- nil
125
147
  end
126
148
  }.compact
127
- ArelExtensions::Nodes::GroupConcat.new [self, sep, order_tabs]
149
+ ArelExtensions::Nodes::GroupConcat.new(self, sep, group: group, order: (order || order_tabs))
128
150
  end
129
151
 
130
152
  #Function returns a string after removing left, right or the both prefixes or suffixes int argument
@@ -155,11 +177,11 @@ module ArelExtensions
155
177
  def not_blank
156
178
  ArelExtensions::Nodes::NotBlank.new [self]
157
179
  end
158
-
159
- def repeat other = 1
180
+
181
+ def repeat other = 1
160
182
  ArelExtensions::Nodes::Repeat.new [self, other]
161
183
  end
162
-
184
+
163
185
  def levenshtein_distance other
164
186
  ArelExtensions::Nodes::LevenshteinDistance.new [self, other]
165
187
  end
@@ -1,12 +1,12 @@
1
1
  namespace :arel_extensions do
2
- desc 'Install DB functions into current DB'
3
- task :install_functions => :environment do
4
- @env_db = if ENV['DB'] == 'oracle' && ((defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx") || (RUBY_PLATFORM == 'java')) # not supported
2
+ desc 'Install DB functions into current DB'
3
+ task :install_functions => :environment do
4
+ @env_db = if ENV['DB'] == 'oracle' && ((defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx") || (RUBY_PLATFORM == 'java')) # not supported
5
5
  (RUBY_PLATFORM == 'java' ? "jdbc-sqlite" : 'sqlite')
6
6
  else
7
7
  ENV['DB'] || ActiveRecord::Base.connection.adapter_name
8
8
  end
9
- ActiveRecord::Base.establish_connection(Rails.env)
9
+ ActiveRecord::Base.establish_connection(Rails.env.to_sym)
10
10
  CommonSqlFunctions.new(ActiveRecord::Base.connection).add_sql_functions(@env_db)
11
- end
11
+ end
12
12
  end
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "1.2.3".freeze
2
+ VERSION = "1.2.14".freeze
3
3
  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.class_eval do
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::IBM_DB.class_eval do
3
+ class Arel::Visitors::IBM_DB
4
4
 
5
5
  def visit_ArelExtensions_Nodes_Ceil o, collector
6
6
  collector << "CEILING("
@@ -400,14 +400,15 @@ module ArelExtensions
400
400
  collector << "(STRING_AGG("
401
401
  collector = visit o.left, collector
402
402
  collector << Arel::Visitors::Oracle::COMMA
403
- if o.right && o.right != 'NULL'
404
- collector = visit o.right, collector
405
- else
406
- collector = visit Arel::Nodes.build_quoted(','), collector
407
- end
403
+ collector =
404
+ if o.separator && o.separator != 'NULL'
405
+ visit o.separator, collector
406
+ else
407
+ visit Arel::Nodes.build_quoted(','), collector
408
+ end
408
409
  collector << ") WITHIN GROUP (ORDER BY "
409
- if !o.orders.blank?
410
- o.orders.each_with_index do |order,i|
410
+ if o.order.present?
411
+ o.order.each_with_index do |order,i|
411
412
  collector << Arel::Visitors::Oracle::COMMA unless i == 0
412
413
  collector = visit order, collector
413
414
  end
@@ -466,8 +467,9 @@ module ArelExtensions
466
467
  Arel::Nodes.build_quoted(1) :
467
468
  ArelExtensions::Nodes::Case.new.when(col<0).then(1).else(0)
468
469
 
469
- if o.scientific_notation
470
- number = ArelExtensions::Nodes::Concat.new([
470
+ number =
471
+ if o.scientific_notation
472
+ ArelExtensions::Nodes::Concat.new([
471
473
  Arel::Nodes::NamedFunction.new('FORMAT',[
472
474
  col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
473
475
  param,
@@ -480,13 +482,13 @@ module ArelExtensions
480
482
  locale
481
483
  ])
482
484
  ])
483
- else
484
- number = Arel::Nodes::NamedFunction.new('FORMAT',[
485
+ else
486
+ Arel::Nodes::NamedFunction.new('FORMAT',[
485
487
  Arel::Nodes.build_quoted(col.abs),
486
488
  param,
487
489
  locale
488
490
  ])
489
- end
491
+ end
490
492
 
491
493
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
492
494
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -548,7 +550,6 @@ module ArelExtensions
548
550
  collector
549
551
  end
550
552
 
551
-
552
553
  end
553
554
  end
554
555
  end
@@ -1,6 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::MySQL.class_eval do
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
@@ -97,26 +97,28 @@ module ArelExtensions
97
97
  end
98
98
 
99
99
  def visit_ArelExtensions_Nodes_Collate o, collector
100
- case o.expressions.first
101
- when Arel::Attributes::Attribute
102
- charset = case o.option
100
+ charset =
101
+ case o.expressions.first
102
+ when Arel::Attributes::Attribute
103
+ case o.option
103
104
  when 'latin1','utf8'
104
105
  o.option
105
106
  else
106
107
  Arel::Table.engine.connection.charset || 'utf8'
107
108
  end
108
- else
109
- charset = (o.option == 'latin1') ? 'latin1' : 'utf8'
110
- end
109
+ else
110
+ (o.option == 'latin1') ? 'latin1' : 'utf8'
111
+ end
111
112
  collector = visit o.expressions.first, collector
112
- if o.ai
113
- collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
113
+ collector <<
114
+ if o.ai
115
+ " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
114
116
  #doesn't work in latin1
115
- elsif o.ci
116
- collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
117
- else
118
- collector << " COLLATE #{charset}_bin"
119
- end
117
+ elsif o.ci
118
+ " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
119
+ else
120
+ " COLLATE #{charset}_bin"
121
+ end
120
122
  collector
121
123
  end
122
124
 
@@ -139,16 +141,16 @@ module ArelExtensions
139
141
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
140
142
  collector << "GROUP_CONCAT("
141
143
  collector = visit o.left, collector
142
- if !o.orders.blank?
144
+ if !o.order.blank?
143
145
  collector << ' ORDER BY '
144
- o.orders.each_with_index do |order,i|
146
+ o.order.each_with_index do |order,i|
145
147
  collector << Arel::Visitors::ToSql::COMMA unless i == 0
146
148
  collector = visit order, collector
147
149
  end
148
150
  end
149
- if o.right && o.right != 'NULL'
151
+ if o.separator && o.separator != 'NULL'
150
152
  collector << ' SEPARATOR '
151
- collector = visit o.right, collector
153
+ collector = visit o.separator, collector
152
154
  end
153
155
  collector << ")"
154
156
  collector
@@ -191,6 +193,13 @@ module ArelExtensions
191
193
  collector
192
194
  end
193
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
+
194
203
  def visit_ArelExtensions_Nodes_Format o, collector
195
204
  case o.col_type
196
205
  when :date, :datetime
@@ -262,7 +271,7 @@ module ArelExtensions
262
271
  else
263
272
  if o.with_interval
264
273
  case o.left
265
- when 'd','m','y'
274
+ when 'd','m','y'
266
275
  interval = 'DAY'
267
276
  when 'h','mn','s'
268
277
  interval = 'SECOND'
@@ -362,20 +371,21 @@ module ArelExtensions
362
371
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
363
372
  sign_length = ArelExtensions::Nodes::Length.new([sign])
364
373
 
365
- if o.scientific_notation
366
- number = ArelExtensions::Nodes::Concat.new([
367
- Arel::Nodes::NamedFunction.new('FORMAT',[
368
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
369
- ]+params),
370
- o.type,
371
- Arel::Nodes::NamedFunction.new('FORMAT',[
372
- col.abs.log10.floor,
373
- 0
374
+ number =
375
+ if o.scientific_notation
376
+ ArelExtensions::Nodes::Concat.new([
377
+ Arel::Nodes::NamedFunction.new('FORMAT',[
378
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
379
+ ]+params),
380
+ o.type,
381
+ Arel::Nodes::NamedFunction.new('FORMAT',[
382
+ col.abs.log10.floor,
383
+ 0
384
+ ])
374
385
  ])
375
- ])
376
- else
377
- number = Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
378
- end
386
+ else
387
+ Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
388
+ end
379
389
 
380
390
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
381
391
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -398,10 +408,34 @@ module ArelExtensions
398
408
  collector
399
409
  end
400
410
 
411
+ def visit_Aggregate_For_AggregateFunction o, collector
412
+ if !window_supported?
413
+ warn("Warning : ArelExtensions: Window Functions are not available in the current version of the DBMS.")
414
+ return collector
415
+ end
416
+
417
+ if !o.order.empty? || !o.group.empty?
418
+ collector << " OVER ("
419
+ if !o.group.empty?
420
+ collector << " PARTITION BY ("
421
+ visit o.group, collector
422
+ collector << ")"
423
+ end
424
+ if !o.order.empty?
425
+ collector << " ORDER BY ("
426
+ visit o.order, collector
427
+ collector << ")"
428
+ end
429
+ collector << ")"
430
+ end
431
+ collector
432
+ end
433
+
401
434
  def visit_ArelExtensions_Nodes_Std o, collector
402
435
  collector << (o.unbiased_estimator ? "STDDEV_SAMP(" : "STDDEV_POP(")
403
436
  visit o.left, collector
404
437
  collector << ")"
438
+ visit_Aggregate_For_AggregateFunction o, collector
405
439
  collector
406
440
  end
407
441
 
@@ -409,15 +443,35 @@ module ArelExtensions
409
443
  collector << (o.unbiased_estimator ? "VAR_SAMP(" : "VAR_POP(")
410
444
  visit o.left, collector
411
445
  collector << ")"
446
+ visit_Aggregate_For_AggregateFunction o, collector
412
447
  collector
413
448
  end
414
449
 
415
450
  # JSON if implemented only after 10.2.3 in MariaDb and 5.7 in MySql
416
451
  def json_supported?
417
- Arel::Table.engine.connection.send(:mariadb?) &&
418
- Arel::Table.engine.connection.send(:version) >= '10.2.3' ||
419
- !Arel::Table.engine.connection.send(:mariadb?) &&
420
- Arel::Table.engine.connection.send(:version) >= '5.7.0'
452
+ version_supported?('10.2.3', '5.7.0')
453
+ end
454
+
455
+ def window_supported?
456
+ version_supported?('10.2.3', '8.0')
457
+ end
458
+
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')
464
+ conn = Arel::Table.engine.connection
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)
421
475
  end
422
476
 
423
477
  def visit_ArelExtensions_Nodes_Json o,collector