arel_extensions 2.1.5 → 2.1.6

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.
data/README.md CHANGED
@@ -1,16 +1,15 @@
1
1
  # Arel Extensions
2
2
 
3
3
  ![GitHub workflow](https://github.com/Faveod/arel-extensions/actions/workflows/ruby.yml/badge.svg)
4
- [![AppVeyor Build Status](https://img.shields.io/appveyor/ci/yazfav/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/yazfav/arel-extensions)
5
- [![Security](https://hakiri.io/github/Faveod/arel-extensions/master.svg)](https://hakiri.io/github/Faveod/arel-extensions/master)
4
+ [![AppVeyor Build Status](https://img.shields.io/appveyor/ci/jdelporte/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/jdelporte/arel-extensions)
6
5
  ![](http://img.shields.io/badge/license-MIT-brightgreen.svg)
7
6
 
8
7
  Gem: [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
9
8
  [![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)](https://rubygems.org/gems/arel_extensions)
10
9
  [![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)](https://rubygems.org/gems/arel_extensions)
11
10
 
12
- Arel Extensions adds shortcuts, fixes and new ORM mappings (ruby to SQL) to Arel.
13
- It aims to ensure pure ruby syntax for the biggest number of usual cases.
11
+ Arel Extensions adds shortcuts, fixes and new ORM mappings (Ruby to SQL) to Arel.
12
+ It aims to ensure pure Ruby syntax for most usual cases.
14
13
  It allows to use more advanced SQL functions for any supported RDBMS.
15
14
 
16
15
 
@@ -293,6 +292,15 @@ User.connection.execute(insert_manager.to_sql)
293
292
  <td class="ok">✔</td>
294
293
  <td class="ok">✔</td>
295
294
  </tr>
295
+ <tr>
296
+ <td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
297
+ <td class="ok">✔</td>
298
+ <td class="ok">✔</td>
299
+ <td class="ok">✔</td>
300
+ <td class="ok">✔</td>
301
+ <td class="ok">✔</td>
302
+ <td class="ko">not implemented</td>
303
+ </tr>
296
304
  <tr>
297
305
  <td class="tg-yw4l">RAND<br>Arel.rand</td>
298
306
  <td class="ok">✔</td>
@@ -321,24 +329,33 @@ User.connection.execute(insert_manager.to_sql)
321
329
  <td class="ok">✔</td>
322
330
  </tr>
323
331
  <tr>
324
- <td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
325
- <td class="ok">✔</td>
332
+ <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
333
+ <td class="tg-yw4l">CONCAT<br>column + "string"</td>
326
334
  <td class="ok">✔</td>
327
335
  <td class="ok">✔</td>
336
+ <td class="tg-j6lv"> ||</td>
328
337
  <td class="ok">✔</td>
338
+ <td class="tg-j6lv">+</td>
329
339
  <td class="ok">✔</td>
330
- <td class="ko">not implemented</td>
331
340
  </tr>
332
341
  <tr>
333
- <th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
334
- <td class="tg-yw4l">CONCAT<br>column + "string"</td>
342
+ <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
335
343
  <td class="ok">✔</td>
336
344
  <td class="ok">✔</td>
337
- <td class="tg-j6lv"> ||</td>
345
+ <td class="tg-orpl">Ruby function</td>
346
+ <td class="ok">✔</td>
338
347
  <td class="ok">✔</td>
339
- <td class="tg-j6lv">+</td>
340
348
  <td class="ok">✔</td>
341
349
  </tr>
350
+ <tr>
351
+ <td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
352
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
353
+ <td class="ok">✔</td>
354
+ <td class="ok">✔</td>
355
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
356
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
357
+ <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
358
+ </tr>
342
359
  <tr>
343
360
  <td class="tg-yw4l">LENGTH<br>column.length</td>
344
361
  <td class="ok">✔</td>
@@ -358,30 +375,57 @@ User.connection.execute(insert_manager.to_sql)
358
375
  <td class="ok">✔</td>
359
376
  </tr>
360
377
  <tr>
361
- <td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
362
- <td class="ok">✔</td>
363
- <td class="tg-j6lv">SUBSTR()</td>
364
- <td class="tg-j6lv">SUBSTR()</td>
365
- <td class="tg-j6lv">SUBSTR()</td>
378
+ <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
366
379
  <td class="ok">✔</td>
380
+ <td class="tg-j6lv">unaccent required</td>
381
+ <td class="tg-j6lv">not supported</td>
367
382
  <td class="ok">✔</td>
383
+ <td class="tg-j6lv">✔</td>
384
+ <td class="tg-j6lv">?</td>
368
385
  </tr>
369
386
  <tr>
370
- <td class="tg-yw4l">FIND_IN_SET<br>column &amp; ("l")</td>
371
- <td class="ok">✔</td>
372
- <td class="ok">✔</td>
373
- <td class="tg-orpl">Ruby function</td>
387
+ <td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
388
+ <td class="ok">not supported</td>
389
+ <td class="tg-j6lv">not supported</td>
390
+ <td class="tg-j6lv">not supported</td>
391
+ <td class="ok">not supported</td>
392
+ <td class="tg-j6lv">✔</td>
393
+ <td class="tg-j6lv">?</td>
394
+ </tr>
395
+ <tr>
396
+ <td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
397
+ <td class="ok">not supported</td>
398
+ <td class="tg-j6lv">✔</td>
399
+ <td class="tg-j6lv">✔</td>
374
400
  <td class="ok">✔</td>
401
+ <td class="tg-j6lv">✔</td>
402
+ <td class="tg-j6lv">?</td>
403
+ </tr>
404
+ <tr>
405
+ <td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
375
406
  <td class="ok">✔</td>
407
+ <td class="tg-j6lv">✔</td>
408
+ <td class="tg-j6lv">not supported</td>
376
409
  <td class="ok">✔</td>
410
+ <td class="tg-j6lv">✔</td>
411
+ <td class="tg-j6lv">?</td>
377
412
  </tr>
378
413
  <tr>
379
- <td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
414
+ <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
380
415
  <td class="ok">✔</td>
381
- <td class="tg-3oug">require fuzzystrmatch</td>
416
+ <td class="ok">✔<br></td>
417
+ <td class="tg-3oug">require pcre.so</td>
418
+ <td class="tg-j6lv">NOT REGEXP_LIKE </td>
419
+ <td class="tg-j6lv">NOT LIKE</td>
382
420
  <td class="ok">✔</td>
421
+ </tr>
422
+ <tr>
423
+ <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
383
424
  <td class="ok">✔</td>
384
425
  <td class="ok">✔</td>
426
+ <td class="tg-3oug">require pcre.so</td>
427
+ <td class="tg-j6lv">REGEXP_LIKE</td>
428
+ <td class="tg-j6lv">LIKE</td>
385
429
  <td class="ok">✔</td>
386
430
  </tr>
387
431
  <tr>
@@ -394,31 +438,22 @@ User.connection.execute(insert_manager.to_sql)
394
438
  <td class="ok">✔</td>
395
439
  </tr>
396
440
  <tr>
397
- <td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
441
+ <td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
398
442
  <td class="ok">✔</td>
443
+ <td class="tg-3oug">require fuzzystrmatch</td>
399
444
  <td class="ok">✔</td>
400
- <td class="tg-3oug">require pcre.so</td>
401
- <td class="tg-j6lv">REGEXP_LIKE</td>
402
- <td class="tg-j6lv">LIKE</td>
403
445
  <td class="ok">✔</td>
404
- </tr>
405
- <tr>
406
- <td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
407
446
  <td class="ok">✔</td>
408
- <td class="ok">✔<br></td>
409
- <td class="tg-3oug">require pcre.so</td>
410
- <td class="tg-j6lv">NOT REGEXP_LIKE </td>
411
- <td class="tg-j6lv">NOT LIKE</td>
412
447
  <td class="ok">✔</td>
413
448
  </tr>
414
449
  <tr>
415
- <td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
416
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
450
+ <td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
451
+ <td class="ok">✔</td>
452
+ <td class="tg-j6lv">SUBSTR()</td>
453
+ <td class="tg-j6lv">SUBSTR()</td>
454
+ <td class="tg-j6lv">SUBSTR()</td>
417
455
  <td class="ok">✔</td>
418
456
  <td class="ok">✔</td>
419
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
420
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
421
- <td class="tg-j6lv">LOWER() LIKE LOWER()</td>
422
457
  </tr>
423
458
  <tr>
424
459
  <td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
@@ -447,43 +482,6 @@ User.connection.execute(insert_manager.to_sql)
447
482
  <td class="tg-j6lv">LTRIM(RTRIM())</td>
448
483
  <td class="tg-j6lv">TRIM()</td>
449
484
  </tr>
450
- <tr>
451
- <td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
452
- <td class="ok">✔</td>
453
- <td class="tg-j6lv">unaccent required</td>
454
- <td class="tg-j6lv">not supported</td>
455
- <td class="ok">✔</td>
456
- <td class="tg-j6lv">✔</td>
457
- <td class="tg-j6lv">?</td>
458
- </tr>
459
- <tr>
460
- <td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
461
- <td class="ok">not supported</td>
462
- <td class="tg-j6lv">not supported</td>
463
- <td class="tg-j6lv">not supported</td>
464
- <td class="ok">not supported</td>
465
- <td class="tg-j6lv">✔</td>
466
- <td class="tg-j6lv">?</td>
467
- </tr>
468
- <tr>
469
- <td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
470
- <td class="ok">not supported</td>
471
- <td class="tg-j6lv">✔</td>
472
- <td class="tg-j6lv">✔</td>
473
- <td class="ok">✔</td>
474
- <td class="tg-j6lv">✔</td>
475
- <td class="tg-j6lv">?</td>
476
- </tr>
477
- <tr>
478
- <td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
479
- <td class="ok">✔</td>
480
- <td class="tg-j6lv">✔</td>
481
- <td class="tg-j6lv">not supported</td>
482
- <td class="ok">✔</td>
483
- <td class="tg-j6lv">✔</td>
484
- <td class="tg-j6lv">?</td>
485
- </tr>
486
-
487
485
  <tr>
488
486
  <th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
489
487
  <td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
@@ -540,7 +538,16 @@ User.connection.execute(insert_manager.to_sql)
540
538
  <td class="ok">✔</td>
541
539
  </tr>
542
540
  <tr>
543
- <th class="tg-72dn" rowspan="8"><div>Comparators functions</div></th>
541
+ <th class="tg-72dn" rowspan="13"><div>Comparators functions</div></th>
542
+ <td class="tg-yw4l">BLANK<br>column.blank<br></td>
543
+ <td class="ok">✔</td>
544
+ <td class="ok">✔</td>
545
+ <td class="ok">✔</td>
546
+ <td class="ok">✔</td>
547
+ <td class="ok">✔</td>
548
+ <td class="ok">✔</td>
549
+ </tr>
550
+ <tr>
544
551
  <td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
545
552
  <td class="ok">✔</td>
546
553
  <td class="ok">✔</td>
@@ -549,6 +556,24 @@ User.connection.execute(insert_manager.to_sql)
549
556
  <td class="ok">✔</td>
550
557
  <td class="ok">✔</td>
551
558
  </tr>
559
+ <tr>
560
+ <td class="tg-yw4l">COALESCE_BLANK<br>column.coalesce_blank(var)</td>
561
+ <td class="ok">✔</td>
562
+ <td class="ok">✔</td>
563
+ <td class="ok">✔</td>
564
+ <td class="ok">✔</td>
565
+ <td class="ok">✔</td>
566
+ <td class="ok">✔</td>
567
+ </tr>
568
+ <tr>
569
+ <td class="tg-yw4l">IF_PRESENT</td>
570
+ <td class="ok">✔</td>
571
+ <td class="ok">✔</td>
572
+ <td class="ok">✔</td>
573
+ <td class="ok">✔</td>
574
+ <td class="ok">✔</td>
575
+ <td class="ok">✔</td>
576
+ </tr>
552
577
  <tr>
553
578
  <td class="tg-yw4l">ISNULL<br>column.isnull()</td>
554
579
  <td class="tg-j6lv">IFNULL()</td>
@@ -558,6 +583,24 @@ User.connection.execute(insert_manager.to_sql)
558
583
  <td class="ok">✔</td>
559
584
  <td class="ok">✔</td>
560
585
  </tr>
586
+ <tr>
587
+ <td class="tg-yw4l">NOT_BLANK<br>column.not_blank<br></td>
588
+ <td class="ok">✔</td>
589
+ <td class="ok">✔</td>
590
+ <td class="ok">✔</td>
591
+ <td class="ok">✔</td>
592
+ <td class="ok">✔</td>
593
+ <td class="ok">✔</td>
594
+ </tr>
595
+ <tr>
596
+ <td class="tg-yw4l">PRESENT<br>column.present<br>alias to NOT_BLANK<br></td>
597
+ <td class="ok">✔</td>
598
+ <td class="ok">✔</td>
599
+ <td class="ok">✔</td>
600
+ <td class="ok">✔</td>
601
+ <td class="ok">✔</td>
602
+ <td class="ok">✔</td>
603
+ </tr>
561
604
  <tr>
562
605
  <td class="tg-yw4l">==<br>column == integer</td>
563
606
  <td class="ok">✔</td>
data/appveyor.yml CHANGED
@@ -7,6 +7,15 @@ cache:
7
7
 
8
8
  environment:
9
9
  matrix:
10
+ - RUBY_VERSION: 25-x64
11
+ RAILS: 4_2
12
+ SQL: MSSQL$SQL2012SP1
13
+ - RUBY_VERSION: 25-x64
14
+ RAILS: 4_2
15
+ SQL: MSSQL$SQL2014
16
+ - RUBY_VERSION: 25-x64
17
+ RAILS: 4_2
18
+ SQL: MSSQL$SQL2016
10
19
  - RUBY_VERSION: 25-x64
11
20
  RAILS: 5_2
12
21
  SQL: MSSQL$SQL2012SP1
@@ -23,6 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency('activerecord', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '>= 6.3.1')
27
26
  s.add_development_dependency('rake', '~> 12.3.3')
28
27
  end
@@ -3,6 +3,13 @@ source 'https://rubygems.org'
3
3
  gem 'arel', '~> 6.0'
4
4
 
5
5
  group :development, :test do
6
+ # We need to explicitly include bigdecimal for ruby 2.7 .
7
+ # See https://github.com/ruby/bigdecimal for details.
8
+ rb_version = Gem::Version.new(RUBY_VERSION)
9
+ if Gem::Version.new('2.7') <= rb_version && rb_version < Gem::Version.new('2.8')
10
+ gem 'bigdecimal', '~> 1.3.5', platforms: %i[mri mingw x64_mingw mswin]
11
+ end
12
+
6
13
  gem 'activesupport', '~> 4.0'
7
14
  gem 'activemodel', '~> 4.0'
8
15
  gem 'activerecord', '~> 4.0'
@@ -11,15 +18,17 @@ group :development, :test do
11
18
  gem 'mysql2', '0.4.10', platforms: %i[mri mswin mingw]
12
19
  gem 'pg', '< 1.0.0', platforms: %i[mri mingw]
13
20
 
14
- gem 'tiny_tds', platforms: %i[mri mingw mswin] if RUBY_PLATFORM.match?(/windows/)
15
- gem 'activerecord-sqlserver-adapter', '~> 4.2.0', platforms: %i[mri mingw mswin] if RUBY_PLATFORM.match?(/windows/)
21
+ gem 'tiny_tds', platforms: %i[mri mingw x64_mingw mswin]
22
+ gem 'activerecord-sqlserver-adapter', '~> 4.2.0', platforms: %i[mri mingw x64_mingw mswin]
16
23
 
17
24
  gem 'ruby-oci8', platforms: %i[mri mswin mingw] if ENV.has_key? 'ORACLE_HOME'
18
25
  gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0' if ENV.has_key? 'ORACLE_HOME'
19
26
 
20
27
  # for JRuby
21
28
  gem 'activerecord-jdbc-adapter', '~> 1.3', platforms: :jruby
22
- gem 'jdbc-sqlite3', platforms: :jruby
29
+ gem 'jdbc-sqlite3', '~> 3.28', platforms: :jruby
30
+ gem 'jdbc-postgres', '~> 42.2', platforms: :jruby
31
+ gem 'jdbc-mysql', '~> 5.1', platforms: :jruby
23
32
  gem 'activerecord-jdbcsqlite3-adapter', platforms: :jruby
24
33
  gem 'activerecord-jdbcmysql-adapter', platforms: :jruby
25
34
  gem 'activerecord-jdbcpostgresql-adapter', platforms: :jruby
File without changes
@@ -23,6 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency('arel', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '>= 6.3.1')
27
26
  s.add_development_dependency('rake', '~> 12.3.3')
28
27
  end
@@ -23,6 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency('activerecord', '>= 6.0')
24
24
 
25
25
  s.add_development_dependency('minitest', '~> 5.9')
26
- s.add_development_dependency('rdoc', '>= 6.3.1')
27
26
  s.add_development_dependency('rake', '~> 12.3.3')
28
27
  end
@@ -35,12 +35,13 @@ module ArelExtensions
35
35
  include Arel::Predications
36
36
  include Arel::OrderPredications
37
37
  include ArelExtensions::Aliases
38
- include ArelExtensions::Math
39
38
  include ArelExtensions::Comparators
40
- include ArelExtensions::Predications
39
+ include ArelExtensions::DateDuration
40
+ include ArelExtensions::Math
41
41
  include ArelExtensions::MathFunctions
42
- include ArelExtensions::StringFunctions
43
42
  include ArelExtensions::NullFunctions
43
+ include ArelExtensions::Predications
44
+ include ArelExtensions::StringFunctions
44
45
 
45
46
  def return_type
46
47
  obj = if @conditions.length > 0
@@ -0,0 +1,36 @@
1
+ # The following is a patch to activerecord when it doesn't
2
+ # have RollUp defined, i.e. for rails < 5.2
3
+
4
+ begin
5
+ Arel::Nodes.const_get('RollUp')
6
+ rescue NameError => e
7
+ module Arel
8
+ module Nodes
9
+ class RollUp < Arel::Nodes::Unary
10
+ end
11
+ end
12
+ end
13
+
14
+ module Arel
15
+ module Visitors
16
+ class PostgreSQL
17
+ # Utilized by GroupingSet, Cube & RollUp visitors to
18
+ # handle grouping aggregation semantics
19
+ def grouping_array_or_grouping_element(o, collector)
20
+ if o.expr.is_a? Array
21
+ collector << "( "
22
+ visit o.expr, collector
23
+ collector << " )"
24
+ else
25
+ visit o.expr, collector
26
+ end
27
+ end
28
+
29
+ def visit_Arel_Nodes_RollUp(o, collector)
30
+ collector << "ROLLUP"
31
+ grouping_array_or_grouping_element o, collector
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ module Nodes
3
+ class SelectCore
4
+ # havings did not exist in rails < 5.2
5
+ if !method_defined?(:havings)
6
+ alias :havings :having
7
+ end
8
+ end
9
+ end
10
+ end
@@ -3,6 +3,12 @@ require 'arel_extensions/nodes/is_null'
3
3
 
4
4
  module ArelExtensions
5
5
  module NullFunctions
6
+
7
+ # if_present returns nil if the the value is nil or blank
8
+ def if_present
9
+ Arel.when(self.cast(:string).present).then(self)
10
+ end
11
+
6
12
  # ISNULL function lets you return an alternative value when an expression is NULL.
7
13
  def is_null
8
14
  ArelExtensions::Nodes::IsNull.new [self]
@@ -19,5 +25,15 @@ module ArelExtensions
19
25
  args.unshift(self)
20
26
  ArelExtensions::Nodes::Coalesce.new args
21
27
  end
28
+
29
+ def coalesce_blank *args
30
+ res = Arel.when(self.cast(:string).present).then(self)
31
+ args[0...-1].each do |a|
32
+ val = a.is_a?(Arel::Nodes::Node) ? a : Arel.quoted(a)
33
+ res = res.when(val.present).then(a)
34
+ end
35
+ res = res.else(args[-1])
36
+ res
37
+ end
22
38
  end
23
39
  end
@@ -184,6 +184,7 @@ module ArelExtensions
184
184
  def not_blank
185
185
  ArelExtensions::Nodes::NotBlank.new [self]
186
186
  end
187
+ alias present not_blank
187
188
 
188
189
  def repeat other = 1
189
190
  ArelExtensions::Nodes::Repeat.new [self, other]
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = '2.1.5'.freeze
2
+ VERSION = '2.1.6'.freeze
3
3
  end
@@ -49,6 +49,82 @@ module ArelExtensions
49
49
  'YYYY-MM-DDTHH:MM:SS:MMM' => 126
50
50
  }.freeze
51
51
 
52
+ # Quoting in JRuby + AR < 5 requires special handling for MSSQL.
53
+ #
54
+ # It relied on @connection.quote, which in turn relied on column type for
55
+ # quoting. We need only to rely on the value type.
56
+ #
57
+ # It didn't handle numbers correctly: `quote(1, nil)` translated into
58
+ # `N'1'` which we don't want.
59
+ #
60
+ # The following is adapted from activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
61
+ #
62
+ if RUBY_PLATFORM == 'java' && ActiveRecord::VERSION::MAJOR < 5
63
+ def quote_string(s)
64
+ s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
65
+ end
66
+
67
+ def quoted_binary(value) # :nodoc:
68
+ "'#{quote_string(value.to_s)}'"
69
+ end
70
+
71
+ def quoted_date(value)
72
+ if value.acts_like?(:time)
73
+ if ActiveRecord::Base.default_timezone == :utc
74
+ value = value.getutc if value.respond_to?(:getutc) && !value.utc?
75
+ else
76
+ value = value.getlocal if value.respond_to?(:getlocal)
77
+ end
78
+ end
79
+
80
+ result = value.to_s(:db)
81
+ if value.respond_to?(:usec) && value.usec > 0
82
+ result << '.' << sprintf('%06d', value.usec)
83
+ else
84
+ result
85
+ end
86
+ end
87
+
88
+ def quoted_true
89
+ 'TRUE'
90
+ end
91
+
92
+ def quoted_false
93
+ 'FALSE'
94
+ end
95
+
96
+ def quoted_time(value) # :nodoc:
97
+ value = value.change(year: 2000, month: 1, day: 1)
98
+ quoted_date(value).sub(/\A\d{4}-\d{2}-\d{2} /, "")
99
+ end
100
+
101
+ def quote value, column = nil
102
+ case value
103
+ when Arel::Nodes::SqlLiteral
104
+ value
105
+ when String, Symbol, ActiveSupport::Multibyte::Chars
106
+ "'#{quote_string(value.to_s)}'"
107
+ when true
108
+ quoted_true
109
+ when false
110
+ quoted_false
111
+ when nil
112
+ 'NULL'
113
+ # BigDecimals need to be put in a non-normalized form and quoted.
114
+ when BigDecimal
115
+ value.to_s('F')
116
+ when Numeric, ActiveSupport::Duration
117
+ value.to_s
118
+ when Date, Time
119
+ "'#{quoted_date(value)}'"
120
+ when Class
121
+ "'#{value}'"
122
+ else
123
+ raise TypeError, "can't quote #{value.class.name}"
124
+ end
125
+ end
126
+ end
127
+
52
128
  # Math Functions
53
129
  def visit_ArelExtensions_Nodes_Ceil o, collector
54
130
  collector << 'CEILING('
@@ -487,6 +563,11 @@ module ArelExtensions
487
563
  collector
488
564
  end
489
565
 
566
+ def visit_Arel_Nodes_RollUp(o, collector)
567
+ collector << "ROLLUP"
568
+ grouping_array_or_grouping_element o, collector
569
+ end
570
+
490
571
  # TODO;
491
572
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
492
573
  collector << '(STRING_AGG('
@@ -647,6 +728,18 @@ module ArelExtensions
647
728
  collector << ')'
648
729
  collector
649
730
  end
731
+
732
+ # Utilized by GroupingSet, Cube & RollUp visitors to
733
+ # handle grouping aggregation semantics
734
+ def grouping_array_or_grouping_element(o, collector)
735
+ if o.expr.is_a? Array
736
+ collector << "( "
737
+ visit o.expr, collector
738
+ collector << " )"
739
+ else
740
+ visit o.expr, collector
741
+ end
742
+ end
650
743
  end
651
744
  end
652
745
  end