arel_extensions 1.2.2 → 1.2.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) 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/blank.rb +14 -11
  29. data/lib/arel_extensions/nodes/case.rb +8 -4
  30. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  31. data/lib/arel_extensions/nodes/change_case.rb +2 -2
  32. data/lib/arel_extensions/nodes/coalesce.rb +2 -2
  33. data/lib/arel_extensions/nodes/collate.rb +12 -12
  34. data/lib/arel_extensions/nodes/concat.rb +6 -13
  35. data/lib/arel_extensions/nodes/date_diff.rb +3 -5
  36. data/lib/arel_extensions/nodes/duration.rb +0 -2
  37. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  38. data/lib/arel_extensions/nodes/floor.rb +0 -0
  39. data/lib/arel_extensions/nodes/format.rb +8 -8
  40. data/lib/arel_extensions/nodes/formatted_number.rb +23 -23
  41. data/lib/arel_extensions/nodes/function.rb +10 -0
  42. data/lib/arel_extensions/nodes/is_null.rb +10 -8
  43. data/lib/arel_extensions/nodes/json.rb +28 -30
  44. data/lib/arel_extensions/nodes/length.rb +0 -0
  45. data/lib/arel_extensions/nodes/levenshtein_distance.rb +5 -5
  46. data/lib/arel_extensions/nodes/locate.rb +7 -7
  47. data/lib/arel_extensions/nodes/matches.rb +4 -4
  48. data/lib/arel_extensions/nodes/power.rb +6 -5
  49. data/lib/arel_extensions/nodes/rand.rb +0 -0
  50. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  51. data/lib/arel_extensions/nodes/replace.rb +24 -6
  52. data/lib/arel_extensions/nodes/round.rb +5 -5
  53. data/lib/arel_extensions/nodes/soundex.rb +16 -15
  54. data/lib/arel_extensions/nodes/std.rb +19 -21
  55. data/lib/arel_extensions/nodes/substring.rb +8 -15
  56. data/lib/arel_extensions/nodes/sum.rb +7 -0
  57. data/lib/arel_extensions/nodes/trim.rb +3 -3
  58. data/lib/arel_extensions/nodes/union.rb +2 -3
  59. data/lib/arel_extensions/nodes/union_all.rb +1 -2
  60. data/lib/arel_extensions/nodes/wday.rb +0 -0
  61. data/lib/arel_extensions/null_functions.rb +2 -2
  62. data/lib/arel_extensions/predications.rb +35 -33
  63. data/lib/arel_extensions/set_functions.rb +16 -16
  64. data/lib/arel_extensions/string_functions.rb +34 -12
  65. data/lib/arel_extensions/tasks.rb +5 -5
  66. data/lib/arel_extensions/version.rb +1 -1
  67. data/lib/arel_extensions/visitors.rb +1 -1
  68. data/lib/arel_extensions/visitors/ibm_db.rb +1 -1
  69. data/lib/arel_extensions/visitors/mssql.rb +18 -17
  70. data/lib/arel_extensions/visitors/mysql.rb +92 -46
  71. data/lib/arel_extensions/visitors/oracle.rb +40 -28
  72. data/lib/arel_extensions/visitors/oracle12.rb +1 -1
  73. data/lib/arel_extensions/visitors/postgresql.rb +80 -34
  74. data/lib/arel_extensions/visitors/sqlite.rb +54 -46
  75. data/lib/arel_extensions/visitors/to_sql.rb +75 -62
  76. data/test/arelx_test_helper.rb +28 -0
  77. data/test/real_db_test.rb +1 -1
  78. data/test/support/fake_record.rb +4 -0
  79. data/test/test_comparators.rb +9 -8
  80. data/test/visitors/test_bulk_insert_oracle.rb +8 -7
  81. data/test/visitors/test_bulk_insert_sqlite.rb +9 -8
  82. data/test/visitors/test_bulk_insert_to_sql.rb +8 -10
  83. data/test/visitors/test_oracle.rb +41 -40
  84. data/test/visitors/test_to_sql.rb +367 -193
  85. data/test/with_ar/all_agnostic_test.rb +85 -39
  86. data/test/with_ar/insert_agnostic_test.rb +3 -2
  87. data/test/with_ar/test_bulk_sqlite.rb +6 -5
  88. data/test/with_ar/test_math_sqlite.rb +4 -4
  89. data/test/with_ar/test_string_mysql.rb +4 -6
  90. data/test/with_ar/test_string_sqlite.rb +3 -7
  91. data/version_v1.rb +3 -0
  92. data/version_v2.rb +3 -0
  93. metadata +13 -7
  94. data/test/helper.rb +0 -18
@@ -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'
@@ -284,22 +293,14 @@ module ArelExtensions
284
293
 
285
294
  def visit_ArelExtensions_Nodes_IsNull o, collector
286
295
  collector << "ISNULL("
287
- collector = visit o.left, collector
288
- if o.right
289
- collector << Arel::Visitors::MySQL::COMMA
290
- collector = visit o.right, collector
291
- end
296
+ collector = visit o.expr, collector
292
297
  collector << ")"
293
298
  collector
294
299
  end
295
300
 
296
301
  def visit_ArelExtensions_Nodes_IsNotNull o, collector
297
302
  collector << "NOT ISNULL("
298
- collector = visit o.left, collector
299
- if o.right
300
- collector << Arel::Visitors::MySQL::COMMA
301
- collector = visit o.right, collector
302
- end
303
+ collector = visit o.expr, collector
303
304
  collector << ")"
304
305
  collector
305
306
  end
@@ -370,20 +371,21 @@ module ArelExtensions
370
371
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
371
372
  sign_length = ArelExtensions::Nodes::Length.new([sign])
372
373
 
373
- if o.scientific_notation
374
- number = ArelExtensions::Nodes::Concat.new([
375
- Arel::Nodes::NamedFunction.new('FORMAT',[
376
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
377
- ]+params),
378
- o.type,
379
- Arel::Nodes::NamedFunction.new('FORMAT',[
380
- col.abs.log10.floor,
381
- 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
+ ])
382
385
  ])
383
- ])
384
- else
385
- number = Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
386
- end
386
+ else
387
+ Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
388
+ end
387
389
 
388
390
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
389
391
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -406,10 +408,34 @@ module ArelExtensions
406
408
  collector
407
409
  end
408
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
+
409
434
  def visit_ArelExtensions_Nodes_Std o, collector
410
435
  collector << (o.unbiased_estimator ? "STDDEV_SAMP(" : "STDDEV_POP(")
411
436
  visit o.left, collector
412
437
  collector << ")"
438
+ visit_Aggregate_For_AggregateFunction o, collector
413
439
  collector
414
440
  end
415
441
 
@@ -417,15 +443,35 @@ module ArelExtensions
417
443
  collector << (o.unbiased_estimator ? "VAR_SAMP(" : "VAR_POP(")
418
444
  visit o.left, collector
419
445
  collector << ")"
446
+ visit_Aggregate_For_AggregateFunction o, collector
420
447
  collector
421
448
  end
422
449
 
423
450
  # JSON if implemented only after 10.2.3 in MariaDb and 5.7 in MySql
424
451
  def json_supported?
425
- Arel::Table.engine.connection.send(:mariadb?) &&
426
- Arel::Table.engine.connection.send(:version) >= '10.2.3' ||
427
- !Arel::Table.engine.connection.send(:mariadb?) &&
428
- 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)
429
475
  end
430
476
 
431
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.class_eval do
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'}
@@ -131,14 +131,15 @@ module ArelExtensions
131
131
  collector << "(LISTAGG("
132
132
  collector = visit o.left, collector
133
133
  collector << Arel::Visitors::Oracle::COMMA
134
- if o.right && o.right != 'NULL'
135
- collector = visit o.right, collector
136
- else
137
- collector = visit Arel::Nodes.build_quoted(','), collector
138
- end
134
+ collector =
135
+ if o.separator && o.separator != 'NULL'
136
+ visit o.separator, collector
137
+ else
138
+ visit Arel::Nodes.build_quoted(','), collector
139
+ end
139
140
  collector << ") WITHIN GROUP (ORDER BY "
140
- if !o.orders.blank?
141
- o.orders.each_with_index do |order,i|
141
+ if !o.order.blank?
142
+ o.order.each_with_index do |order,i|
142
143
  collector << Arel::Visitors::Oracle::COMMA unless i == 0
143
144
  collector = visit order, collector
144
145
  end
@@ -154,8 +155,8 @@ module ArelExtensions
154
155
  o.expressions.each_with_index { |arg, i|
155
156
  collector << Arel::Visitors::Oracle::COMMA unless i == 0
156
157
  if i > 0 && o.left_node_type == :text
157
- if arg == ''
158
- collector << 'empty_clob()'
158
+ if arg == '' || (arg.is_a?(Arel::Nodes::Quoted) && (arg.expr == ''))
159
+ collector << "NULL"
159
160
  else
160
161
  collector << 'TO_CLOB('
161
162
  collector = visit arg, collector
@@ -257,9 +258,15 @@ module ArelExtensions
257
258
  collector << ")"
258
259
  return collector
259
260
  when :time
260
- collector << "TO_DATE(TO_CHAR("
261
- collector = visit o.left, collector
262
- collector << ",'HH24:MI:SS'),'HH24:MI:SS')"
261
+ if (o.left.respond_to?(:return_type) && o.left.return_type == :string) || o.left.is_a?(Arel::Nodes::Quoted)
262
+ collector << "TO_DATE("
263
+ collector = visit o.left, collector
264
+ collector << ",'HH24:MI:SS')"
265
+ else
266
+ collector << "TO_DATE(TO_CHAR("
267
+ collector = visit o.left, collector
268
+ collector << ",'HH24:MI:SS'),'HH24:MI:SS')"
269
+ end
263
270
  return collector
264
271
  when :number, :decimal
265
272
  collector << "TO_NUMBER("
@@ -269,9 +276,15 @@ module ArelExtensions
269
276
  when :datetime
270
277
  as_attr = Arel::Nodes::SqlLiteral.new('timestamp')
271
278
  when :date
272
- collector << "TO_DATE(TO_CHAR("
273
- collector = visit o.left, collector
274
- collector << ",'YYYY-MM-DD'),'YYYY-MM-DD')"
279
+ if (o.left.respond_to?(:return_type) && o.left.return_type == :string) || o.left.is_a?(Arel::Nodes::Quoted)
280
+ collector << "TO_DATE("
281
+ collector = visit o.left, collector
282
+ collector << ",'YYYY-MM-DD')"
283
+ else
284
+ collector << "TO_DATE(TO_CHAR("
285
+ collector = visit o.left, collector
286
+ collector << ",'YYYY-MM-DD'),'YYYY-MM-DD')"
287
+ end
275
288
  return collector
276
289
  when :binary
277
290
  as_attr = Arel::Nodes::SqlLiteral.new('binary')
@@ -294,13 +307,13 @@ module ArelExtensions
294
307
  end
295
308
 
296
309
  def visit_ArelExtensions_Nodes_IsNull o, collector
297
- collector = visit o.left, collector
310
+ collector = visit o.expr, collector
298
311
  collector << ' IS NULL'
299
312
  collector
300
313
  end
301
314
 
302
315
  def visit_ArelExtensions_Nodes_IsNotNull o, collector
303
- collector = visit o.left, collector
316
+ collector = visit o.expr, collector
304
317
  collector << ' IS NOT NULL'
305
318
  collector
306
319
  end
@@ -407,11 +420,11 @@ module ArelExtensions
407
420
  end
408
421
 
409
422
  def visit_ArelExtensions_Nodes_Blank o, collector
410
- visit o.left.trim.length.coalesce(0).eq(0), collector
423
+ visit o.expr.trim.length.coalesce(0).eq(0), collector
411
424
  end
412
425
 
413
426
  def visit_ArelExtensions_Nodes_NotBlank o, collector
414
- visit o.left.trim.length.coalesce(0).gt(0), collector
427
+ visit o.expr.trim.length.coalesce(0).gt(0), collector
415
428
  end
416
429
 
417
430
  def visit_ArelExtensions_Nodes_DateAdd o, collector
@@ -454,14 +467,12 @@ module ArelExtensions
454
467
  o.left.each_with_index do |row, idx| # values
455
468
  collector << " UNION ALL " if idx != 0
456
469
  collector << "(SELECT "
457
- v = Arel::Nodes::Values.new(row, o.cols)
458
- len = v.expressions.length - 1
459
- v.expressions.each_with_index { |value, i|
470
+ len = row.length - 1
471
+ row.zip(o.cols).each_with_index { |(value, attr), i|
460
472
  case value
461
473
  when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
462
474
  collector = visit value, collector
463
475
  else
464
- attr = v.columns[i]
465
476
  collector << quote(value, attr && column_for(attr)).to_s
466
477
  end
467
478
  collector << Arel::Visitors::Oracle::COMMA unless i == len
@@ -477,12 +488,13 @@ module ArelExtensions
477
488
  o.left.each_with_index do |row, idx|
478
489
  collector << " UNION ALL " if idx != 0
479
490
  collector << "(SELECT "
480
- v = Arel::Nodes::Values.new(row, o.cols)
481
- len = v.expressions.length - 1
482
- 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|
483
493
  case value
484
494
  when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
485
495
  collector = visit value, collector
496
+ when Integer
497
+ collector << value.to_s
486
498
  else
487
499
  collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
488
500
  end
@@ -571,7 +583,7 @@ module ArelExtensions
571
583
  else
572
584
  collector = visit o.left, collector
573
585
  end
574
- quote = o.right.to_s =~ /[^a-zA-Z_]/ ? '"' : ''
586
+ quote = o.right.to_s =~ /(\A["].*["]\z)|\A[a-zA-Z_]*\z/ ? '' : '"'
575
587
  collector << " AS #{quote}"
576
588
  collector = visit o.right, collector
577
589
  collector << "#{quote}"
@@ -68,7 +68,7 @@ module ArelExtensions
68
68
  if i != 0
69
69
  collector << Arel::Visitors::MySQL::COMMA
70
70
  end
71
- collector = visit v, collector
71
+ collector = visit v, collector
72
72
  end
73
73
  collector << ')'
74
74
  when Hash
@@ -1,6 +1,6 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- Arel::Visitors::PostgreSQL.class_eval do
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',
@@ -83,23 +83,49 @@ module ArelExtensions
83
83
  collector
84
84
  end
85
85
 
86
+ def visit_Aggregate_For_AggregateFunction o, collector
87
+ if !o.order.blank? || !o.group.blank?
88
+ collector << " OVER ("
89
+ if !o.group.blank?
90
+ collector << " PARTITION BY "
91
+ o.group.each_with_index do |group, i|
92
+ collector << Arel::Visitors::PostgreSQL::COMMA unless i == 0
93
+ visit group, collector
94
+ end
95
+ end
96
+ if !o.order.blank?
97
+ collector << " ORDER BY "
98
+ o.order.each_with_index do |order, i|
99
+ collector << Arel::Visitors::PostgreSQL::COMMA unless i == 0
100
+ visit order, collector
101
+ end
102
+ end
103
+ collector << ")"
104
+ end
105
+ collector
106
+ end
107
+
86
108
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
87
109
  collector << "array_to_string(array_agg("
88
110
  collector = visit o.left, collector
89
- if !o.orders.blank?
90
- collector << ' ORDER BY '
91
- o.orders.each_with_index do |order,i|
111
+ if o.order && !o.order.blank?
112
+ collector << " ORDER BY"
113
+ o.order.each_with_index do |order, i|
92
114
  collector << Arel::Visitors::PostgreSQL::COMMA unless i == 0
93
- collector = visit order, collector
115
+ collector << " "
116
+ visit order, collector
94
117
  end
95
118
  end
96
119
  collector << ")"
120
+ o.order = nil
121
+ visit_Aggregate_For_AggregateFunction o, collector
97
122
  collector << Arel::Visitors::PostgreSQL::COMMA
98
- if o.right && o.right != 'NULL'
99
- collector = visit o.right, collector
100
- else
101
- collector = visit Arel::Nodes.build_quoted(','), collector
102
- end
123
+ collector =
124
+ if o.separator && o.separator != 'NULL'
125
+ visit o.separator, collector
126
+ else
127
+ visit Arel::Nodes.build_quoted(','), collector
128
+ end
103
129
  collector << ")"
104
130
  collector
105
131
  end
@@ -277,14 +303,30 @@ module ArelExtensions
277
303
  collector
278
304
  end
279
305
 
306
+ def visit_ArelExtensions_Nodes_RegexpReplace o, collector
307
+ collector << "REGEXP_REPLACE("
308
+ visit o.left, collector
309
+ collector << Arel::Visitors::ToSql::COMMA
310
+ tab = o.pattern.inspect+ 'g' # Make it always global
311
+ pattern = tab.split('/')[1..-2].join('/')
312
+ flags = tab.split('/')[-1]
313
+ visit Arel::Nodes.build_quoted(pattern), collector
314
+ collector << Arel::Visitors::ToSql::COMMA
315
+ visit o.substitute, collector
316
+ collector << Arel::Visitors::ToSql::COMMA
317
+ visit Arel::Nodes.build_quoted(flags+"g"), collector
318
+ collector << ")"
319
+ collector
320
+ end
321
+
280
322
  def visit_ArelExtensions_Nodes_IsNull o, collector
281
- collector = visit o.left, collector
323
+ collector = visit o.expr, collector
282
324
  collector << ' IS NULL'
283
325
  collector
284
326
  end
285
327
 
286
328
  def visit_ArelExtensions_Nodes_IsNotNull o, collector
287
- collector = visit o.left, collector
329
+ collector = visit o.expr, collector
288
330
  collector << ' IS NOT NULL'
289
331
  collector
290
332
  end
@@ -293,6 +335,7 @@ module ArelExtensions
293
335
  collector << "sum("
294
336
  collector = visit o.expr, collector
295
337
  collector << ")"
338
+ visit_Aggregate_For_AggregateFunction o, collector
296
339
  collector
297
340
  end
298
341
 
@@ -345,27 +388,28 @@ module ArelExtensions
345
388
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
346
389
  sign_length = ArelExtensions::Nodes::Length.new([sign])
347
390
 
348
- if o.scientific_notation
349
- number = ArelExtensions::Nodes::Concat.new([
350
- Arel::Nodes::NamedFunction.new('TRIM',[
351
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
352
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
353
- Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
354
- ])]),
355
- o.type,
356
- Arel::Nodes::NamedFunction.new('TRIM',[
357
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
358
- col.abs.log10.floor,
359
- Arel::Nodes.build_quoted('FM'+nines_before)
360
- ])])
361
- ])
362
- else
363
- number = Arel::Nodes::NamedFunction.new('TRIM',[
364
- Arel::Nodes::NamedFunction.new('TO_CHAR',[
365
- Arel::Nodes.build_quoted(col.abs),
366
- Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
367
- ])])
368
- end
391
+ number =
392
+ if o.scientific_notation
393
+ ArelExtensions::Nodes::Concat.new([
394
+ Arel::Nodes::NamedFunction.new('TRIM',[
395
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
396
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
397
+ Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
398
+ ])]),
399
+ o.type,
400
+ Arel::Nodes::NamedFunction.new('TRIM',[
401
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
402
+ col.abs.log10.floor,
403
+ Arel::Nodes.build_quoted('FM'+nines_before)
404
+ ])])
405
+ ])
406
+ else
407
+ Arel::Nodes::NamedFunction.new('TRIM',[
408
+ Arel::Nodes::NamedFunction.new('TO_CHAR',[
409
+ Arel::Nodes.build_quoted(col.abs),
410
+ Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
411
+ ])])
412
+ end
369
413
 
370
414
  repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
371
415
  when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
@@ -420,6 +464,7 @@ module ArelExtensions
420
464
  collector << (o.unbiased_estimator ? "STDDEV_SAMP(" : "STDDEV_POP(")
421
465
  visit o.left, collector
422
466
  collector << ")"
467
+ visit_Aggregate_For_AggregateFunction o, collector
423
468
  collector
424
469
  end
425
470
 
@@ -427,6 +472,7 @@ module ArelExtensions
427
472
  collector << (o.unbiased_estimator ? "VAR_SAMP(" : "VAR_POP(")
428
473
  visit o.left, collector
429
474
  collector << ")"
475
+ visit_Aggregate_For_AggregateFunction o, collector
430
476
  collector
431
477
  end
432
478
 
@@ -438,7 +484,7 @@ module ArelExtensions
438
484
  if i != 0
439
485
  collector << Arel::Visitors::MySQL::COMMA
440
486
  end
441
- collector = visit v, collector
487
+ collector = visit v, collector
442
488
  end
443
489
  collector << '])'
444
490
  when Hash