arel_extensions 1.2.2 → 1.2.13

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 (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