arel_extensions 2.0.22 → 2.1.1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +358 -71
  3. data/Gemfile +8 -8
  4. data/README.md +86 -0
  5. data/arel_extensions.gemspec +1 -1
  6. data/gemfiles/rails5_2.gemfile +8 -7
  7. data/gemfiles/rails6.gemfile +7 -8
  8. data/gemfiles/rails6_1.gemfile +6 -7
  9. data/gemfiles/rails7.gemfile +22 -0
  10. data/gemspecs/arel_extensions-v1.gemspec +1 -1
  11. data/gemspecs/arel_extensions-v2.gemspec +1 -1
  12. data/lib/arel_extensions/aliases.rb +14 -0
  13. data/lib/arel_extensions/attributes.rb +2 -0
  14. data/lib/arel_extensions/date_duration.rb +2 -2
  15. data/lib/arel_extensions/helpers.rb +48 -0
  16. data/lib/arel_extensions/insert_manager.rb +19 -17
  17. data/lib/arel_extensions/math.rb +22 -32
  18. data/lib/arel_extensions/nodes/case.rb +5 -6
  19. data/lib/arel_extensions/nodes/cast.rb +1 -1
  20. data/lib/arel_extensions/nodes/date_diff.rb +23 -4
  21. data/lib/arel_extensions/nodes/format.rb +3 -2
  22. data/lib/arel_extensions/nodes/function.rb +2 -6
  23. data/lib/arel_extensions/nodes/json.rb +3 -1
  24. data/lib/arel_extensions/nodes/replace.rb +0 -8
  25. data/lib/arel_extensions/nodes/union.rb +1 -1
  26. data/lib/arel_extensions/nodes/union_all.rb +1 -1
  27. data/lib/arel_extensions/version.rb +1 -1
  28. data/lib/arel_extensions/visitors/mssql.rb +109 -51
  29. data/lib/arel_extensions/visitors/mysql.rb +15 -2
  30. data/lib/arel_extensions/visitors/oracle.rb +6 -1
  31. data/lib/arel_extensions/visitors/postgresql.rb +20 -10
  32. data/lib/arel_extensions/visitors/sqlite.rb +6 -3
  33. data/lib/arel_extensions/visitors/to_sql.rb +13 -8
  34. data/lib/arel_extensions/visitors.rb +9 -1
  35. data/lib/arel_extensions.rb +57 -15
  36. data/test/arelx_test_helper.rb +45 -0
  37. data/test/database.yml +8 -2
  38. data/test/real_db_test.rb +5 -1
  39. data/test/support/fake_record.rb +1 -1
  40. data/test/visitors/test_to_sql.rb +39 -11
  41. data/test/with_ar/all_agnostic_test.rb +79 -6
  42. data/test/with_ar/insert_agnostic_test.rb +6 -2
  43. data/test/with_ar/test_bulk_sqlite.rb +6 -2
  44. data/test/with_ar/test_math_sqlite.rb +6 -2
  45. data/test/with_ar/test_string_mysql.rb +6 -2
  46. data/test/with_ar/test_string_sqlite.rb +6 -2
  47. data/version_v1.rb +1 -1
  48. data/version_v2.rb +1 -1
  49. metadata +10 -8
  50. data/appveyor.yml +0 -44
@@ -2,27 +2,37 @@ module ArelExtensions
2
2
  module Visitors
3
3
  module MSSQL
4
4
 
5
- Arel::Visitors::MSSQL::DATE_MAPPING = {
5
+ mssql_class = Arel::Visitors.constants.select { |c|
6
+ Arel::Visitors.const_get(c).is_a?(Class) && %i[MSSQL SQLServer].include?(c)
7
+ }.first
8
+
9
+ LOADED_VISITOR = Arel::Visitors.const_get(mssql_class) || Arel::Visitors.const_get('MSSQL')
10
+
11
+ LOADED_VISITOR::DATE_MAPPING = {
6
12
  'd' => 'day', 'm' => 'month', 'y' => 'year', 'wd' => 'weekday', 'w' => 'week', 'h' => 'hour', 'mn' => 'minute', 's' => 'second'
7
13
  }.freeze
8
14
 
9
- Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES = {
15
+ LOADED_VISITOR::DATE_FORMAT_DIRECTIVES = {
10
16
  '%Y' => 'YYYY', '%C' => '', '%y' => 'YY', '%m' => 'MM', '%B' => '', '%b' => '', '%^b' => '', # year, month
11
17
  '%d' => 'DD', '%e' => '', '%j' => '', '%w' => 'dw', '%A' => '', # day, weekday
12
18
  '%H' => 'hh', '%k' => '', '%I' => '', '%l' => '', '%P' => '', '%p' => '', # hours
13
19
  '%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
14
20
  }.freeze
15
21
 
16
- Arel::Visitors::MSSQL::DATE_FORMAT_REGEX =
22
+ LOADED_VISITOR::DATE_FORMAT_FORMAT = {
23
+ 'YY' => '0#', 'MM' => '0#', 'DD' => '0#', 'hh' => '0#', 'mi' => '0#', 'ss' => '0#'
24
+ }
25
+
26
+ LOADED_VISITOR::DATE_FORMAT_REGEX =
17
27
  Regexp.new(
18
- Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES
28
+ LOADED_VISITOR::DATE_FORMAT_DIRECTIVES
19
29
  .keys
20
30
  .map{|k| Regexp.escape(k)}
21
31
  .join('|')
22
32
  ).freeze
23
33
 
24
34
  # TODO; all others... http://www.sql-server-helper.com/tips/date-formats.aspx
25
- Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS = {
35
+ LOADED_VISITOR::DATE_CONVERT_FORMATS = {
26
36
  'YYYY-MM-DD' => 120,
27
37
  'YY-MM-DD' => 120,
28
38
  'MM/DD/YYYY' => 101,
@@ -79,7 +89,7 @@ module ArelExtensions
79
89
  def visit_ArelExtensions_Nodes_Concat o, collector
80
90
  collector << "CONCAT("
81
91
  o.expressions.each_with_index { |arg, i|
82
- collector << Arel::Visitors::MSSQL::COMMA if i != 0
92
+ collector << LOADED_VISITOR::COMMA if i != 0
83
93
  collector = visit arg, collector
84
94
  }
85
95
  collector << ")"
@@ -102,23 +112,23 @@ module ArelExtensions
102
112
  case o.right_node_type
103
113
  when :ruby_date, :ruby_time, :date, :datetime, :time
104
114
  collector << case o.left_node_type
105
- when :ruby_time, :datetime, :time then 'DATEDIFF(second'
106
- else 'DATEDIFF(day'
107
- end
108
- collector << Arel::Visitors::MSSQL::COMMA
115
+ when :ruby_time, :datetime, :time then 'DATEDIFF(second'
116
+ else 'DATEDIFF(day'
117
+ end
118
+ collector << LOADED_VISITOR::COMMA
109
119
  collector = visit o.right, collector
110
- collector << Arel::Visitors::MSSQL::COMMA
120
+ collector << LOADED_VISITOR::COMMA
111
121
  collector = visit o.left, collector
112
122
  collector << ')'
113
123
  else
114
124
  da = ArelExtensions::Nodes::DateAdd.new([])
115
125
  collector << "DATEADD("
116
126
  collector = visit da.mssql_datepart(o.right), collector
117
- collector << Arel::Visitors::MSSQL::COMMA
127
+ collector << LOADED_VISITOR::COMMA
118
128
  collector << "-("
119
129
  collector = visit da.mssql_value(o.right), collector
120
130
  collector << ")"
121
- collector << Arel::Visitors::MSSQL::COMMA
131
+ collector << LOADED_VISITOR::COMMA
122
132
  collector = visit o.left, collector
123
133
  collector << ")"
124
134
  collector
@@ -129,9 +139,9 @@ module ArelExtensions
129
139
  def visit_ArelExtensions_Nodes_DateAdd o, collector
130
140
  collector << "DATEADD("
131
141
  collector = visit o.mssql_datepart(o.right), collector
132
- collector << Arel::Visitors::MSSQL::COMMA
142
+ collector << LOADED_VISITOR::COMMA
133
143
  collector = visit o.mssql_value(o.right), collector
134
- collector << Arel::Visitors::MSSQL::COMMA
144
+ collector << LOADED_VISITOR::COMMA
135
145
  collector = visit o.left, collector
136
146
  collector << ")"
137
147
  collector
@@ -144,8 +154,8 @@ module ArelExtensions
144
154
  left = o.left.end_with?('i') ? o.left[0..-2] : o.left
145
155
  conv = ['h', 'mn', 's'].include?(o.left)
146
156
  collector << 'DATEPART('
147
- collector << Arel::Visitors::MSSQL::DATE_MAPPING[left]
148
- collector << Arel::Visitors::MSSQL::COMMA
157
+ collector << LOADED_VISITOR::DATE_MAPPING[left]
158
+ collector << LOADED_VISITOR::COMMA
149
159
  collector << 'CONVERT(datetime,' if conv
150
160
  collector = visit o.right, collector
151
161
  collector << ')' if conv
@@ -155,20 +165,29 @@ module ArelExtensions
155
165
  end
156
166
 
157
167
  def visit_ArelExtensions_Nodes_Length o, collector
158
- collector << "#{o.bytewise ? 'DATALENGTH' : 'LEN'}("
159
- collector = visit o.expr, collector
160
- collector << ")"
161
- collector
168
+ if o.bytewise
169
+ collector << "(DATALENGTH("
170
+ collector = visit o.expr, collector
171
+ collector << ") / ISNULL(NULLIF(DATALENGTH(LEFT(COALESCE("
172
+ collector = visit o.expr, collector
173
+ collector << ", '#' ), 1 )), 0), 1))"
174
+ collector
175
+ else
176
+ collector << "LEN("
177
+ collector = visit o.expr, collector
178
+ collector << ")"
179
+ collector
180
+ end
162
181
  end
163
182
 
164
183
  def visit_ArelExtensions_Nodes_Round o, collector
165
184
  collector << "ROUND("
166
185
  o.expressions.each_with_index { |arg, i|
167
- collector << Arel::Visitors::MSSQL::COMMA if i != 0
186
+ collector << LOADED_VISITOR::COMMA if i != 0
168
187
  collector = visit arg, collector
169
188
  }
170
189
  if o.expressions.length == 1
171
- collector << Arel::Visitors::MSSQL::COMMA
190
+ collector << LOADED_VISITOR::COMMA
172
191
  collector << "0"
173
192
  end
174
193
  collector << ")"
@@ -178,7 +197,7 @@ module ArelExtensions
178
197
  def visit_ArelExtensions_Nodes_Locate o, collector
179
198
  collector << "CHARINDEX("
180
199
  collector = visit o.right, collector
181
- collector << Arel::Visitors::MSSQL::COMMA
200
+ collector << LOADED_VISITOR::COMMA
182
201
  collector = visit o.left, collector
183
202
  collector << ")"
184
203
  collector
@@ -187,28 +206,20 @@ module ArelExtensions
187
206
  def visit_ArelExtensions_Nodes_Substring o, collector
188
207
  collector << 'SUBSTRING('
189
208
  collector = visit o.expressions[0], collector
190
- collector << Arel::Visitors::MSSQL::COMMA
209
+ collector << LOADED_VISITOR::COMMA
191
210
  collector = visit o.expressions[1], collector
192
- collector << Arel::Visitors::MSSQL::COMMA
211
+ collector << LOADED_VISITOR::COMMA
193
212
  collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
194
213
  collector << ')'
195
214
  collector
196
215
  end
197
216
 
198
217
  def visit_ArelExtensions_Nodes_Trim o, collector
199
- if o.right
200
- collector << "REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(REPLACE("
201
- collector = visit o.left, collector
202
- collector << ", ' ', '~'), "
203
- collector = visit o.right, collector
204
- collector << ", ' '))), ' ', "
205
- collector = visit o.right, collector
206
- collector << "), '~', ' ')"
207
- else
208
- collector << "LTRIM(RTRIM("
209
- collector = visit o.left, collector
210
- collector << "))"
211
- end
218
+ collector << 'TRIM( '
219
+ collector = visit o.right, collector
220
+ collector << " FROM "
221
+ collector = visit o.left, collector
222
+ collector << ")"
212
223
  collector
213
224
  end
214
225
 
@@ -255,12 +266,21 @@ module ArelExtensions
255
266
  end
256
267
 
257
268
  def visit_ArelExtensions_Nodes_Format o, collector
258
- f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES)
259
- if fmt = Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f]
269
+ f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, LOADED_VISITOR::DATE_FORMAT_DIRECTIVES)
270
+ if fmt = LOADED_VISITOR::DATE_CONVERT_FORMATS[f]
260
271
  collector << "CONVERT(VARCHAR(#{f.length})"
261
- collector << Arel::Visitors::MSSQL::COMMA
272
+ collector << LOADED_VISITOR::COMMA
273
+ if o.time_zone
274
+ collector << 'CONVERT(datetime'
275
+ collector << LOADED_VISITOR::COMMA
276
+ collector << ' '
277
+ end
262
278
  collector = visit o.left, collector
263
- collector << Arel::Visitors::MSSQL::COMMA
279
+ if o.time_zone
280
+ collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
281
+ collector = visit o.time_zone, collector
282
+ end
283
+ collector << LOADED_VISITOR::COMMA
264
284
  collector << fmt.to_s
265
285
  collector << ')'
266
286
  collector
@@ -272,13 +292,31 @@ module ArelExtensions
272
292
  collector << sep
273
293
  sep = ' + '
274
294
  case
275
- when s.scan(Arel::Visitors::MSSQL::DATE_FORMAT_REGEX)
276
- dir = Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES[s.matched]
277
- collector << 'LTRIM(STR(DATEPART('
295
+ when s.scan(LOADED_VISITOR::DATE_FORMAT_REGEX)
296
+ dir = LOADED_VISITOR::DATE_FORMAT_DIRECTIVES[s.matched]
297
+ fmt = LOADED_VISITOR::DATE_FORMAT_FORMAT[dir]
298
+ collector << 'TRIM('
299
+ collector << 'FORMAT(' if fmt
300
+ collector << 'STR(' if !fmt
301
+ collector << 'DATEPART('
278
302
  collector << dir
279
- collector << Arel::Visitors::MSSQL::COMMA
303
+ collector << LOADED_VISITOR::COMMA
304
+ if o.time_zone
305
+ collector << 'CONVERT(datetime'
306
+ collector << LOADED_VISITOR::COMMA
307
+ collector << ' '
308
+ end
280
309
  collector = visit o.left, collector
281
- collector << ')))'
310
+ if o.time_zone
311
+ collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
312
+ collector = visit o.time_zone, collector
313
+ end
314
+ collector << ')'
315
+ collector << ')' if !fmt
316
+ collector << LOADED_VISITOR::COMMA << "'#{fmt}')" if fmt
317
+ collector << ')'
318
+ when s.scan(/^%%/)
319
+ collector = visit Arel::Nodes.build_quoted('%'), collector
282
320
  when s.scan(/[^%]+|./)
283
321
  collector = visit Arel::Nodes.build_quoted(s.matched), collector
284
322
  end
@@ -291,7 +329,7 @@ module ArelExtensions
291
329
  def visit_ArelExtensions_Nodes_Replace o, collector
292
330
  collector << "REPLACE("
293
331
  o.expressions.each_with_index { |arg, i|
294
- collector << Arel::Visitors::MSSQL::COMMA if i != 0
332
+ collector << LOADED_VISITOR::COMMA if i != 0
295
333
  collector = visit arg, collector
296
334
  }
297
335
  collector << ")"
@@ -301,7 +339,7 @@ module ArelExtensions
301
339
  def visit_ArelExtensions_Nodes_FindInSet o, collector
302
340
  collector << "dbo.FIND_IN_SET("
303
341
  o.expressions.each_with_index { |arg, i|
304
- collector << Arel::Visitors::MSSQL::COMMA if i != 0
342
+ collector << LOADED_VISITOR::COMMA if i != 0
305
343
  collector = visit arg, collector
306
344
  }
307
345
  collector << ")"
@@ -387,6 +425,27 @@ module ArelExtensions
387
425
  collector
388
426
  end
389
427
 
428
+ alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
429
+ def visit_Arel_Nodes_As o, collector
430
+ if o.left.is_a?(Arel::Nodes::Binary)
431
+ collector << '('
432
+ collector = visit o.left, collector
433
+ collector << ')'
434
+ else
435
+ collector = visit o.left, collector
436
+ end
437
+ collector << " AS "
438
+
439
+ # sometimes these values are already quoted, if they are, don't double quote it
440
+ quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
441
+
442
+ collector << '"' if quote
443
+ collector = visit o.right, collector
444
+ collector << '"' if quote
445
+
446
+ collector
447
+ end
448
+
390
449
  # SQL Server does not know about REGEXP
391
450
  def visit_Arel_Nodes_Regexp o, collector
392
451
  collector = visit o.left, collector
@@ -554,7 +613,6 @@ module ArelExtensions
554
613
  collector << ')'
555
614
  collector
556
615
  end
557
-
558
616
  end
559
617
  end
560
618
  end
@@ -209,7 +209,13 @@ module ArelExtensions
209
209
  when :date, :datetime, :time
210
210
  fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
211
211
  collector << "DATE_FORMAT("
212
+ collector << "CONVERT_TZ(" if o.time_zone
212
213
  collector = visit o.left, collector
214
+ if o.time_zone
215
+ collector << COMMA << "'UTC'" << COMMA
216
+ collector = visit o.time_zone, collector
217
+ collector << ')'
218
+ end
213
219
  collector << COMMA
214
220
  collector = visit Arel::Nodes.build_quoted(fmt), collector
215
221
  collector << ")"
@@ -354,9 +360,16 @@ module ArelExtensions
354
360
  else
355
361
  collector = visit o.left, collector
356
362
  end
357
- collector << " AS `"
363
+ collector << " AS "
364
+
365
+ # sometimes these values are already quoted, if they are, don't double quote it
366
+ quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '`' && o.right[-1] != '`'
367
+
368
+ collector << '`' if quote
358
369
  collector = visit o.right, collector
359
- collector << "`"
370
+ collector << '`' if quote
371
+
372
+ collector
360
373
  collector
361
374
  end
362
375
 
@@ -447,7 +447,12 @@ module ArelExtensions
447
447
  def visit_ArelExtensions_Nodes_Format o, collector
448
448
  fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
449
449
  collector << "TO_CHAR("
450
+ collector << "CAST(" if o.time_zone
450
451
  collector = visit o.left, collector
452
+ if o.time_zone
453
+ collector << " as timestamp) at time zone "
454
+ collector = visit o.time_zone, collector
455
+ end
451
456
  collector << COMMA
452
457
  collector = visit Arel::Nodes.build_quoted(fmt), collector
453
458
  collector << ")"
@@ -517,7 +522,7 @@ module ArelExtensions
517
522
  if element.is_a?(Time)
518
523
  ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
519
524
  elsif element.is_a?(Arel::Attributes::Attribute)
520
- col = Arel::Table.engine.connection.schema_cache.columns_hash(element.relation.table_name)[element.name.to_s]
525
+ col = ArelExtensions::column_of(element.relation.table_name, element.name.to_s)
521
526
  if col && (col.type == :time)
522
527
  ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
523
528
  else
@@ -86,9 +86,15 @@ module ArelExtensions
86
86
  else
87
87
  collector = visit o.left, collector
88
88
  end
89
- collector << " AS \""
89
+ collector << " AS "
90
+
91
+ # sometimes these values are already quoted, if they are, don't double quote it
92
+ quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
93
+
94
+ collector << '"' if quote
90
95
  collector = visit o.right, collector
91
- collector << "\""
96
+ collector << '"' if quote
97
+
92
98
  collector
93
99
  end
94
100
 
@@ -170,6 +176,10 @@ module ArelExtensions
170
176
  fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
171
177
  collector << "TO_CHAR("
172
178
  collector = visit o.left, collector
179
+ if o.time_zone
180
+ collector << " AT TIME ZONE "
181
+ collector = visit o.time_zone, collector
182
+ end
173
183
  collector << COMMA
174
184
  collector = visit Arel::Nodes.build_quoted(fmt), collector
175
185
  collector << ")"
@@ -283,9 +293,9 @@ module ArelExtensions
283
293
  return collector
284
294
  end
285
295
  end
286
- collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM "
296
+ collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM CAST("
287
297
  collector = visit o.right, collector
288
- collector << ")"
298
+ collector << " AS TIMESTAMP WITH TIME ZONE))"
289
299
  collector << " * (INTERVAL '1' #{interval})" if interval && o.with_interval
290
300
  collector
291
301
  end
@@ -368,11 +378,13 @@ module ArelExtensions
368
378
  when :number, :decimal, :float
369
379
  Arel::Nodes::SqlLiteral.new('numeric')
370
380
  when :datetime
371
- Arel::Nodes::SqlLiteral.new('timestamp')
381
+ Arel::Nodes::SqlLiteral.new('timestamp with time zone')
372
382
  when :date
373
383
  Arel::Nodes::SqlLiteral.new('date')
374
384
  when :binary
375
385
  Arel::Nodes::SqlLiteral.new('binary')
386
+ when :jsonb
387
+ Arel::Nodes::SqlLiteral.new('jsonb')
376
388
  else
377
389
  Arel::Nodes::SqlLiteral.new(o.as_attr.to_s)
378
390
  end
@@ -401,13 +413,13 @@ module ArelExtensions
401
413
  ArelExtensions::Nodes::Concat.new([
402
414
  Arel::Nodes::NamedFunction.new('TRIM',[
403
415
  Arel::Nodes::NamedFunction.new('TO_CHAR',[
404
- col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
416
+ Arel.when(col.not_eq 0).then(col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)).else(1),
405
417
  Arel::Nodes.build_quoted('FM'+nines_before+'"'+comma+'"V'+nines_after)
406
418
  ])]),
407
419
  o.type,
408
420
  Arel::Nodes::NamedFunction.new('TRIM',[
409
421
  Arel::Nodes::NamedFunction.new('TO_CHAR',[
410
- col.abs.log10.floor,
422
+ Arel.when(col.not_eq 0).then(col.abs.log10.floor).else(0),
411
423
  Arel::Nodes.build_quoted('FM'+nines_before)
412
424
  ])])
413
425
  ])
@@ -511,8 +523,6 @@ module ArelExtensions
511
523
  collector << '::jsonb'
512
524
  when NilClass
513
525
  collector << %Q['null'::jsonb]
514
- when Arel::Attributes::Attribute
515
- collector = visit o.dict.cast(:jsonb), collector
516
526
  else
517
527
  collector = visit o.dict, collector
518
528
  collector << '::jsonb'
@@ -532,7 +542,7 @@ module ArelExtensions
532
542
 
533
543
  def visit_ArelExtensions_Nodes_JsonGet o,collector
534
544
  collector = visit o.dict, collector
535
- collector << ' -> '
545
+ collector << ' ->> '
536
546
  collector = visit o.key, collector
537
547
  collector
538
548
  end
@@ -1,3 +1,5 @@
1
+ require 'arel_extensions/helpers'
2
+
1
3
  module ArelExtensions
2
4
  module Visitors
3
5
  class Arel::Visitors::SQLite
@@ -327,7 +329,7 @@ module ArelExtensions
327
329
  if element.is_a?(Time)
328
330
  return Arel::Nodes::NamedFunction.new('STRFTIME',[element, '%H:%M:%S'])
329
331
  elsif element.is_a?(Arel::Attributes::Attribute)
330
- col = Arel::Table.engine.connection.schema_cache.columns_hash(element.relation.table_name)[element.name.to_s]
332
+ col = ArelExtensions::column_of(element.relation.table_name, element.name.to_s)
331
333
  if col && (col.type == :time)
332
334
  return Arel::Nodes::NamedFunction.new('STRFTIME',[element, '%H:%M:%S'])
333
335
  else
@@ -379,9 +381,10 @@ module ArelExtensions
379
381
  else
380
382
  collector = visit o.left, collector
381
383
  end
382
- collector << " AS \""
384
+ sep = o.right.size > 1 && o.right[0] == '"' && o.right[-1] == '"' ? '' : '"'
385
+ collector << " AS #{sep}"
383
386
  collector = visit o.right, collector
384
- collector << "\""
387
+ collector << "#{sep}"
385
388
  collector
386
389
  end
387
390
 
@@ -8,10 +8,15 @@ module ArelExtensions
8
8
  def make_json_string expr
9
9
  Arel::Nodes.build_quoted('"') \
10
10
  + expr
11
+ .coalesce('')
11
12
  .replace('\\','\\\\').replace('"','\"').replace("\n", '\n') \
12
13
  + '"'
13
14
  end
14
15
 
16
+ def make_json_null
17
+ Arel::Nodes.build_quoted("null")
18
+ end
19
+
15
20
  # Math Functions
16
21
  def visit_ArelExtensions_Nodes_Abs o, collector
17
22
  collector << "ABS("
@@ -601,20 +606,20 @@ module ArelExtensions
601
606
  def json_value(o,v)
602
607
  case o.type_of_node(v)
603
608
  when :string
604
- Arel.when(v.is_null).then(Arel::Nodes.build_quoted("null")).else(make_json_string(v))
609
+ Arel.when(v.is_null).then(make_json_null).else(make_json_string(v))
605
610
  when :date
606
611
  s = v.format('%Y-%m-%d')
607
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
612
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
608
613
  when :datetime
609
614
  s = v.format('%Y-%m-%dT%H:%M:%S')
610
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
615
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
611
616
  when :time
612
617
  s = v.format('%H:%M:%S')
613
- Arel.when(s.is_null).then(Arel::Nodes.build_quoted("null")).else(Arel::Nodes.build_quoted('"') + s + '"')
618
+ Arel.when(s.is_null).then(make_json_null).else(make_json_string(s))
614
619
  when :nil
615
- Arel::Nodes.build_quoted("null")
620
+ make_json_null
616
621
  else
617
- ArelExtensions::Nodes::Cast.new([v, :string]).coalesce("null")
622
+ ArelExtensions::Nodes::Cast.new([v, :string]).coalesce(make_json_null)
618
623
  end
619
624
  end
620
625
 
@@ -636,7 +641,7 @@ module ArelExtensions
636
641
  if i != 0
637
642
  res += ', '
638
643
  end
639
- res += make_json_string(ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("")) + ': '
644
+ res += make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
640
645
  res += json_value(o,v)
641
646
  end
642
647
  res += '}'
@@ -658,7 +663,7 @@ module ArelExtensions
658
663
  if i != 0
659
664
  res = res + ', '
660
665
  end
661
- kv = make_json_string(ArelExtensions::Nodes::Cast.new([k, :string]).coalesce("")) + ': '
666
+ kv = make_json_string(ArelExtensions::Nodes::Cast.new([k, :string])) + ': '
662
667
  kv += json_value(o,v)
663
668
  res = res + kv.group_concat(', ', order: Array(orders)).coalesce('')
664
669
  end
@@ -9,9 +9,17 @@ if defined?(Arel::Visitors::Oracle)
9
9
  require 'arel_extensions/visitors/oracle12'
10
10
  end
11
11
 
12
- if defined?(Arel::Visitors::MSSQL)
12
+ if defined?(Arel::Visitors::SQLServer) || defined?(Arel::Visitors::MSSQL)
13
13
  require 'arel_extensions/visitors/mssql'
14
+ end
14
15
 
16
+ if defined?(Arel::Visitors::SQLServer)
17
+ class Arel::Visitors::SQLServer
18
+ include ArelExtensions::Visitors::MSSQL
19
+ end
20
+ end
21
+
22
+ if defined?(Arel::Visitors::MSSQL)
15
23
  class Arel::Visitors::MSSQL
16
24
  include ArelExtensions::Visitors::MSSQL
17
25