arel_extensions 2.3.3 → 2.4.0

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +92 -43
  3. data/.rubocop.yml +4 -4
  4. data/Makefile +18 -0
  5. data/NEWS.md +7 -0
  6. data/README.md +21 -3
  7. data/dev/arelx.dockerfile +13 -16
  8. data/dev/compose.yaml +26 -28
  9. data/gemfiles/rails8.gemfile +40 -0
  10. data/gemfiles/rails8_1.gemfile +41 -0
  11. data/lib/arel_extensions/constants.rb +13 -0
  12. data/lib/arel_extensions/helpers.rb +3 -4
  13. data/lib/arel_extensions/math_functions.rb +1 -1
  14. data/lib/arel_extensions/nodes/byte_size.rb +11 -0
  15. data/lib/arel_extensions/nodes/case.rb +1 -1
  16. data/lib/arel_extensions/nodes/char_length.rb +11 -0
  17. data/lib/arel_extensions/nodes/function.rb +1 -3
  18. data/lib/arel_extensions/nodes/matches.rb +2 -2
  19. data/lib/arel_extensions/predications.rb +1 -1
  20. data/lib/arel_extensions/string_functions.rb +11 -1
  21. data/lib/arel_extensions/version.rb +1 -1
  22. data/lib/arel_extensions/visitors/mssql.rb +23 -8
  23. data/lib/arel_extensions/visitors/mysql.rb +14 -0
  24. data/lib/arel_extensions/visitors/oracle.rb +15 -1
  25. data/lib/arel_extensions/visitors/postgresql.rb +14 -0
  26. data/lib/arel_extensions/visitors/sqlite.rb +17 -1
  27. data/lib/arel_extensions/visitors/to_sql.rb +1 -1
  28. data/lib/arel_extensions/visitors.rb +1 -1
  29. data/lib/arel_extensions.rb +15 -6
  30. data/test/arelx_test_helper.rb +8 -6
  31. data/test/config_loader.rb +9 -0
  32. data/test/database.yml +8 -6
  33. data/test/real_db_test.rb +2 -2
  34. data/test/support/fake_record.rb +3 -1
  35. data/test/visitors/test_bulk_insert_oracle.rb +2 -2
  36. data/test/visitors/test_bulk_insert_sqlite.rb +2 -2
  37. data/test/with_ar/all_agnostic_test.rb +65 -17
  38. data/test/with_ar/insert_agnostic_test.rb +2 -2
  39. data/test/with_ar/test_bulk_sqlite.rb +2 -2
  40. data/test/with_ar/test_math_sqlite.rb +2 -2
  41. data/test/with_ar/test_string_mysql.rb +2 -2
  42. data/test/with_ar/test_string_sqlite.rb +2 -2
  43. data/version_v1.rb +1 -1
  44. data/version_v2.rb +1 -1
  45. metadata +9 -5
  46. data/bin/compose +0 -6
  47. data/gemfiles/rails3.gemfile +0 -20
  48. data/gemfiles/rails4_2.gemfile +0 -38
@@ -28,9 +28,7 @@ module ArelExtensions
28
28
 
29
29
  def as other
30
30
  res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
31
- if Gem::Version.new(Arel::VERSION) >= Gem::Version.new('9.0.0')
32
- self.alias = Arel.sql(other)
33
- end
31
+ self.alias = Arel.sql(other)
34
32
  res
35
33
  end
36
34
 
@@ -1,11 +1,11 @@
1
1
  module ArelExtensions
2
2
  module Nodes
3
3
  class IMatches < Arel::Nodes::Matches
4
- attr_accessor :case_sensitive if Arel::VERSION.to_i < 7
4
+ attr_accessor :case_sensitive if AREL_VERSION < V7
5
5
 
6
6
  def initialize(left, right, escape = nil)
7
7
  r = Arel.quoted(right)
8
- if Arel::VERSION.to_i < 7 # managed by default in version 7+ (rails 5), so useful for rails 3 & 4
8
+ if AREL_VERSION < V7 # managed by default in version 7+ (rails 5), so useful for rails 3 & 4
9
9
  super(left, r, escape)
10
10
  @case_sensitive = false
11
11
  else
@@ -5,7 +5,7 @@ module ArelExtensions
5
5
  end
6
6
 
7
7
  def matches(other, escape = nil, case_sensitive = nil)
8
- if Arel::VERSION.to_i < 7
8
+ if AREL_VERSION < V7
9
9
  Arel::Nodes::Matches.new(self, Arel.quoted(other), escape)
10
10
  else
11
11
  Arel::Nodes::Matches.new(self, Arel.quoted(other), escape, case_sensitive)
@@ -1,3 +1,5 @@
1
+ require 'arel_extensions/nodes/byte_size'
2
+ require 'arel_extensions/nodes/char_length'
1
3
  require 'arel_extensions/nodes/concat' # if Arel::VERSION.to_i < 7
2
4
  require 'arel_extensions/nodes/length'
3
5
  require 'arel_extensions/nodes/locate'
@@ -19,6 +21,8 @@ require 'arel_extensions/nodes/md5'
19
21
 
20
22
  module ArelExtensions
21
23
  module StringFunctions
24
+ include ArelExtensions::Warning
25
+
22
26
  # *FindInSet function .......
23
27
  def &(other)
24
28
  ArelExtensions::Nodes::FindInSet.new [
@@ -29,15 +33,21 @@ module ArelExtensions
29
33
 
30
34
  # LENGTH function returns the length (bytewise) of the value in a text field.
31
35
  def length
36
+ deprecated "Use `byte_size` or `char_length` instead. `length` relies on the vendor's `LEN/LENGTH` implementation and it's not portable"
32
37
  ArelExtensions::Nodes::Length.new self, true
33
38
  end
34
39
 
35
40
  def byte_length
41
+ deprecated "Use `byte_size` instead. `byte_length` relies on the vendor's `LEN/LENGTH` implementation and it's not portable"
36
42
  ArelExtensions::Nodes::Length.new self, true
37
43
  end
38
44
 
45
+ def byte_size
46
+ ArelExtensions::Nodes::ByteSize.new self
47
+ end
48
+
39
49
  def char_length
40
- ArelExtensions::Nodes::Length.new self, false
50
+ ArelExtensions::Nodes::CharLength.new self
41
51
  end
42
52
 
43
53
  # LOCATE function returns the first starting position of a string in another string.
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = '2.3.3'.freeze
2
+ VERSION = '2.4.0'.freeze
3
3
  end
@@ -76,7 +76,7 @@ module ArelExtensions
76
76
  # The following is adapted from
77
77
  # https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
78
78
  #
79
- if RUBY_PLATFORM == 'java' && Arel::VERSION.to_i <= 6
79
+ if RUBY_PLATFORM == 'java' && AREL_VERSION <= V6
80
80
  def quote_string(s)
81
81
  s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
82
82
  end
@@ -133,7 +133,7 @@ module ArelExtensions
133
133
  value.to_s('F')
134
134
  when Numeric, ActiveSupport::Duration
135
135
  value.to_s
136
- when Arel::VERSION.to_i > 6 && ActiveRecord::Type::Time::Value
136
+ when AREL_VERSION > V6 && ActiveRecord::Type::Time::Value
137
137
  "'#{quoted_time(value)}'"
138
138
  when Date, Time
139
139
  "'#{quoted_date(value)}'"
@@ -216,8 +216,6 @@ module ArelExtensions
216
216
  collector
217
217
  end
218
218
 
219
-
220
-
221
219
  def visit_ArelExtensions_Nodes_DateDiff o, collector
222
220
  case o.right_node_type
223
221
  when :ruby_date, :ruby_time, :date, :datetime, :time
@@ -274,6 +272,23 @@ module ArelExtensions
274
272
  collector
275
273
  end
276
274
 
275
+ def visit_ArelExtensions_Nodes_ByteSize o, collector
276
+ collector << 'DATALENGTH(CAST('
277
+ collector = visit o.expr.coalesce(''), collector
278
+ collector << ' AS VARCHAR(MAX)))'
279
+ collector
280
+ end
281
+
282
+ def visit_ArelExtensions_Nodes_CharLength o, collector
283
+ # According to the docs https://learn.microsoft.com/en-us/sql/t-sql/functions/len-transact-sql?view=sql-server-ver17
284
+ # LEN doesn't take into account the trailing white space, and that's why
285
+ # we need to do acrobatics.
286
+ collector << 'LEN('
287
+ collector = visit o.expr.coalesce(''), collector
288
+ collector << " + 'x') - 1"
289
+ collector
290
+ end
291
+
277
292
  def visit_ArelExtensions_Nodes_Length o, collector
278
293
  if o.bytewise
279
294
  collector << '(DATALENGTH('
@@ -575,11 +590,11 @@ module ArelExtensions
575
590
  # Sometimes these values are already quoted, if they are, don't double quote it
576
591
  lft, rgt =
577
592
  if o.right.is_a?(Arel::Nodes::SqlLiteral)
578
- if Arel::VERSION.to_i >= 6 && o.right[0] != '[' && o.right[-1] != ']'
593
+ if AREL_VERSION >= V6 && o.right[0] != '[' && o.right[-1] != ']'
579
594
  # This is a lie, it's not about arel version, but SQL Server's (>= 2000).
580
- ['[', ']']
581
- elsif o.right[0] != '"' && o.right[-1] != '"'
582
- ['"', '"']
595
+ %w([ ])
596
+ elsif ACTIVE_RECORD_VERSION < V8_1 && o.right[0] != '"' && o.right[-1] != '"'
597
+ %w(" ")
583
598
  else
584
599
  []
585
600
  end
@@ -86,6 +86,20 @@ module ArelExtensions
86
86
  end
87
87
 
88
88
  # String functions
89
+ def visit_ArelExtensions_Nodes_ByteSize o, collector
90
+ collector << 'LENGTH('
91
+ collector = visit o.expr.coalesce(''), collector
92
+ collector << ')'
93
+ collector
94
+ end
95
+
96
+ def visit_ArelExtensions_Nodes_CharLength o, collector
97
+ collector << 'CHAR_LENGTH('
98
+ collector = visit o.expr.coalesce(''), collector
99
+ collector << ')'
100
+ collector
101
+ end
102
+
89
103
  def visit_ArelExtensions_Nodes_IMatches o, collector # insensitive on ASCII
90
104
  collector << 'LOWER('
91
105
  collector = visit o.left, collector
@@ -13,6 +13,20 @@ module ArelExtensions
13
13
  }
14
14
  NUMBER_COMMA_MAPPING = {'en_US' => '.,', 'fr_FR' => ',', 'sv_SE' => ', '}
15
15
 
16
+ def visit_ArelExtensions_Nodes_ByteSize o, collector
17
+ collector << 'LENGTHB('
18
+ collector = visit o.expr.coalesce(''), collector
19
+ collector << ')'
20
+ collector
21
+ end
22
+
23
+ def visit_ArelExtensions_Nodes_CharLength o, collector
24
+ collector << 'LENGTH('
25
+ collector = visit o.expr.coalesce(''), collector
26
+ collector << ')'
27
+ collector
28
+ end
29
+
16
30
  def visit_ArelExtensions_Nodes_Log10 o, collector
17
31
  collector << 'LOG('
18
32
  o.expressions.each_with_index { |arg, i|
@@ -489,7 +503,7 @@ module ArelExtensions
489
503
  end
490
504
 
491
505
  # add primary_key if not present, avoid zip
492
- if Arel::VERSION.to_i < 7
506
+ if AREL_VERSION < V7
493
507
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
494
508
  collector << '('
495
509
  o.left.each_with_index do |row, idx| # values
@@ -20,6 +20,20 @@ module ArelExtensions
20
20
  'en_US' => '.,', 'fr_FR' => ',', 'sv_SE' => ', '
21
21
  }.freeze
22
22
 
23
+ def visit_ArelExtensions_Nodes_ByteSize o, collector
24
+ collector << 'octet_length('
25
+ collector = visit o.expr.coalesce(''), collector
26
+ collector << ')'
27
+ collector
28
+ end
29
+
30
+ def visit_ArelExtensions_Nodes_CharLength o, collector
31
+ collector << 'length('
32
+ collector = visit o.expr.coalesce(''), collector
33
+ collector << ')'
34
+ collector
35
+ end
36
+
23
37
  def visit_ArelExtensions_Nodes_Rand o, collector
24
38
  collector << 'RANDOM('
25
39
  if (o.left != nil && o.right != nil)
@@ -18,6 +18,22 @@ module ArelExtensions
18
18
  }.freeze
19
19
 
20
20
  # String functions
21
+ def visit_ArelExtensions_Nodes_ByteSize o, collector
22
+ # sqlite 3.43.0 (2023-08-24) introduced `octet_length`, but we still support older versions.
23
+ # https://sqlite.org/changes.html
24
+ collector << 'length(CAST('
25
+ collector = visit o.expr.coalesce(''), collector
26
+ collector << ' AS BLOB))'
27
+ collector
28
+ end
29
+
30
+ def visit_ArelExtensions_Nodes_CharLength o, collector
31
+ collector << 'length('
32
+ collector = visit o.expr.coalesce(''), collector
33
+ collector << ')'
34
+ collector
35
+ end
36
+
21
37
  def visit_ArelExtensions_Nodes_IMatches o, collector # insensitive on ASCII
22
38
  collector = visit o.left.ci_collate, collector
23
39
  collector << ' LIKE '
@@ -237,7 +253,7 @@ module ArelExtensions
237
253
  collector
238
254
  end
239
255
 
240
- if Arel::VERSION.to_i < 7
256
+ if AREL_VERSION < V7
241
257
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
242
258
  o.left.each_with_index do |row, idx|
243
259
  collector << 'SELECT '
@@ -462,7 +462,7 @@ module ArelExtensions
462
462
  collector
463
463
  end
464
464
 
465
- if Arel::VERSION.to_i < 7
465
+ if AREL_VERSION < V7
466
466
  def visit_ArelExtensions_InsertManager_BulkValues o, collector
467
467
  collector << 'VALUES '
468
468
  row_nb = o.left.length
@@ -14,7 +14,7 @@ if RUBY_PLATFORM == 'java' \
14
14
  warn 'arel/visitors/sqlserver not found: MSSQL might not work correctly.'
15
15
  end
16
16
  elsif RUBY_PLATFORM != 'java' \
17
- && Arel::VERSION.to_i < 10 \
17
+ && ArelExtensions::AREL_VERSION < ArelExtensions::V10 \
18
18
  && Gem::Specification.find { |g| g.name == 'activerecord-sqlserver-adapter' }
19
19
  begin
20
20
  require 'arel_sqlserver'
@@ -1,4 +1,5 @@
1
1
  require 'arel'
2
+ require 'arel_extensions/constants'
2
3
  require 'base64'
3
4
 
4
5
  require 'arel_extensions/railtie' if defined?(Rails::Railtie)
@@ -17,7 +18,7 @@ class Arel::Nodes::Casted
17
18
  include Arel::AliasPredication
18
19
 
19
20
  # They forget to define hash.
20
- if Gem::Version.new(Arel::VERSION) < Gem::Version.new('10.0.0')
21
+ if ArelExtensions::AREL_VERSION < ArelExtensions::V10
21
22
  def hash
22
23
  [self.class, self.val, self.attribute].hash
23
24
  end
@@ -45,7 +46,7 @@ class Arel::Nodes::Function
45
46
  include Arel::Expressions
46
47
  end
47
48
 
48
- if Gem::Version.new(Arel::VERSION) >= Gem::Version.new('7.1.0')
49
+ if ArelExtensions::AREL_VERSION >= ArelExtensions::V7_1
49
50
  class Arel::Nodes::Case
50
51
  include Arel::Math
51
52
  include Arel::Expressions
@@ -199,12 +200,20 @@ class Arel::Nodes::Function
199
200
  include ArelExtensions::NullFunctions
200
201
  include ArelExtensions::Predications
201
202
 
203
+ if ArelExtensions::ACTIVE_RECORD_VERSION >= ArelExtensions::V8_1
204
+ attr_accessor :alias
205
+ alias_method :old_initialize, :initialize
206
+
207
+ def initialize(expr, aliaz = nil)
208
+ old_initialize(expr)
209
+ self.alias = aliaz
210
+ end
211
+ end
212
+
202
213
  alias_method(:old_as, :as) rescue nil
203
214
  def as other
204
215
  res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
205
- if Gem::Version.new(Arel::VERSION) >= Gem::Version.new('9.0.0')
206
- self.alias = Arel.sql(other)
207
- end
216
+ self.alias = Arel.sql(other)
208
217
  res
209
218
  end
210
219
  end
@@ -325,7 +334,7 @@ class Arel::Nodes::Node
325
334
  end
326
335
 
327
336
  require 'active_record'
328
- if ActiveRecord.version >= Gem::Version.create('7.2')
337
+ if ArelExtensions::ACTIVE_RECORD_VERSION >= ArelExtensions::V7_2
329
338
  class ActiveRecord::Relation::WhereClause
330
339
  def except_predicates(columns)
331
340
  attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
@@ -1,11 +1,13 @@
1
- require 'rubygems'
2
- require 'minitest/autorun'
3
- require 'fileutils'
4
- require 'arel'
5
1
  require 'active_record'
6
-
2
+ require 'arel'
3
+ require 'arel_extensions/constants'
4
+ require 'fileutils'
5
+ require 'minitest/autorun'
6
+ require 'rubygems'
7
7
  require 'support/fake_record'
8
8
 
9
+ require_relative './config_loader'
10
+
9
11
  ENV['AREL_EXTENSIONS_IN_TEST'] = '1' # Useful for deprecation warnings.
10
12
 
11
13
  def colored(color, msg)
@@ -59,7 +61,7 @@ end
59
61
 
60
62
 
61
63
  def load_lib(gems)
62
- if gems && (RUBY_PLATFORM == 'java' || Arel::VERSION.to_i > 9)
64
+ if gems && (RUBY_PLATFORM == 'java' || ArelExtensions::AREL_VERSION > ArelExtensions::V9_0)
63
65
  gems.each do |gem|
64
66
  begin
65
67
  require gem
@@ -0,0 +1,9 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
4
+ module ConfigLoader
5
+ def self.load(path)
6
+ yaml_content = ERB.new(File.read(path)).result
7
+ YAML.load(yaml_content)
8
+ end
9
+ end
data/test/database.yml CHANGED
@@ -1,3 +1,4 @@
1
+ # Updated to use ENV vars mapped in compose.yml
1
2
  sqlite:
2
3
  adapter: sqlite3
3
4
  database: ":memory:"
@@ -10,27 +11,28 @@ mysql:
10
11
  adapter: mysql2
11
12
  database: arelx_test
12
13
  username: root
13
- host: 127.0.0.1
14
+ host: <%= ENV.fetch('MYSQL_HOST', '127.0.0.1') %>
14
15
  port: 3306
15
- encoding: utf8
16
+ encoding: utf8mb4
16
17
  jdbc-mysql:
17
18
  adapter: jdbcmysql
18
19
  database: arelx_test
19
20
  username: root
20
- encoding: utf8
21
+ encoding: utf8mb4
22
+ host: <%= ENV.fetch('MYSQL_HOST', '127.0.0.1') %>
21
23
  postgresql:
22
24
  adapter: postgresql
23
25
  database: arelx_test
24
26
  username: postgres
25
27
  password: secret
26
- host: 127.0.0.1
28
+ host: <%= ENV.fetch('POSTGRES_HOST', '127.0.0.1') %>
27
29
  port: 5432
28
30
  jdbc-postgresql:
29
31
  adapter: jdbcpostgresql
30
32
  database: arelx_test
31
33
  username: postgres
32
34
  password: secret
33
- host: 127.0.0.1
35
+ host: <%= ENV.fetch('POSTGRES_HOST', '127.0.0.1') %>
34
36
  port: 5432
35
37
  oracle:
36
38
  adapter: oracle_enhanced
@@ -48,7 +50,7 @@ ibm_db:
48
50
  database: arelx_test
49
51
  mssql:
50
52
  adapter: sqlserver
51
- host: localhost
53
+ host: <%= ENV.fetch('MSSQL_HOST', 'localhost') %>
52
54
  database: master
53
55
  username: sa
54
56
  password: Password12!
data/test/real_db_test.rb CHANGED
@@ -6,9 +6,9 @@ $:.unshift "#{File.dirname(__FILE__)}../../lib"
6
6
  require 'arel_extensions'
7
7
 
8
8
  def setup_db
9
- ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
9
+ ActiveRecord::Base.configurations = ConfigLoader.load('test/database.yml')
10
10
  ActiveRecord::Base.establish_connection(ENV['DB'].try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
11
- if ActiveRecord::VERSION::MAJOR >= 7
11
+ if ACTIVE_RECORD_VERSION >= V7_0
12
12
  ActiveRecord.default_timezone = :utc
13
13
  else
14
14
  ActiveRecord::Base.default_timezone = :utc
@@ -1,3 +1,5 @@
1
+ require 'arel_extensions/constants'
2
+
1
3
  module FakeRecord
2
4
  class Column < Struct.new(:name, :type)
3
5
  end
@@ -146,7 +148,7 @@ module FakeRecord
146
148
  connection_pool.connection
147
149
  end
148
150
 
149
- if ActiveRecord.version >= Gem::Version.create('7.2')
151
+ if ArelExtensions::ACTIVE_RECORD_VERSION >= ArelExtensions::V7_2
150
152
  def with_connection(*args, **kwargs, &block)
151
153
  connection_pool.with_connection(*args, **kwargs, &block)
152
154
  end
@@ -15,7 +15,7 @@ module ArelExtensions
15
15
  end
16
16
 
17
17
  def compile node
18
- if Arel::VERSION.to_i > 5
18
+ if AREL_VERSION > V5
19
19
  @visitor.accept(node, Arel::Collectors::SQLString.new).value
20
20
  else
21
21
  @visitor.accept(node)
@@ -23,7 +23,7 @@ module ArelExtensions
23
23
  end
24
24
 
25
25
  it 'should import large set of data in Oracle' do
26
- insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new.into(@table) : Arel::InsertManager.new(@conn).into(@table)
26
+ insert_manager = AREL_VERSION > V6 ? Arel::InsertManager.new.into(@table) : Arel::InsertManager.new(@conn).into(@table)
27
27
  insert_manager.bulk_insert(@cols, @data)
28
28
  _(compile(insert_manager.ast))
29
29
  .must_be_like %Q[INSERT INTO "users" ("name", "comments", "created_at")
@@ -16,7 +16,7 @@ module ArelExtensions
16
16
  end
17
17
 
18
18
  def compile node
19
- if Arel::VERSION.to_i > 5
19
+ if AREL_VERSION > V5
20
20
  @visitor.accept(node, Arel::Collectors::SQLString.new).value
21
21
  else
22
22
  @visitor.accept(node)
@@ -24,7 +24,7 @@ module ArelExtensions
24
24
  end
25
25
 
26
26
  it 'should import large set of data' do
27
- insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new.into(@table) : Arel::InsertManager.new(@conn).into(@table)
27
+ insert_manager = AREL_VERSION > V6 ? Arel::InsertManager.new.into(@table) : Arel::InsertManager.new(@conn).into(@table)
28
28
  insert_manager.bulk_insert(@cols, @data)
29
29
  _(compile(insert_manager.ast))
30
30
  .must_be_like %Q[INSERT INTO "users" ("id", "name", "comments", "created_at")
@@ -1,11 +1,27 @@
1
1
  require 'arelx_test_helper'
2
2
  require 'date'
3
3
 
4
+ # class ActiveRecord::ConnectionAdapters::SQLServerAdapter
5
+ # # We use *args to avoid breaking signature changes between versions.
6
+ # # internal_exec_sql_query(sql, name = "SQL", binds = [], prepare: false)
7
+ # def internal_exec_sql_query(*args)
8
+ # sql = args.first
9
+ # puts "[DEBUG] MSSQL Query: #{sql}\n"
10
+
11
+ # # Call original implementation (defined in the DatabaseStatements module included in this class)
12
+ # super
13
+ # rescue => e
14
+ # # Catch the crash to print the specific query that caused it one last time
15
+ # puts "[CRASH]: #{args.first}\n"
16
+ # raise e
17
+ # end
18
+ # end
19
+
4
20
  module ArelExtensions
5
21
  module WithAr
6
22
  class ListTest < Minitest::Test
7
23
  def connect_db
8
- ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
24
+ ActiveRecord::Base.configurations = ConfigLoader.load('test/database.yml')
9
25
  if ENV['DB'] == 'oracle' && ((defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx') || (RUBY_PLATFORM == 'java')) # not supported
10
26
  @env_db = (RUBY_PLATFORM == 'java' ? 'jdbc-sqlite' : 'sqlite')
11
27
  skip "Platform not supported (DB: #{ENV['DB']}, RUBY_ENGINE: #{RUBY_ENGINE}, RUBY_PLATFORM: #{RUBY_PLATFORM})"
@@ -13,7 +29,7 @@ module ArelExtensions
13
29
  @env_db = ENV['DB']
14
30
  end
15
31
  ActiveRecord::Base.establish_connection(@env_db.try(:to_sym) || (RUBY_PLATFORM == 'java' ? :"jdbc-sqlite" : :sqlite))
16
- if ActiveRecord::VERSION::MAJOR >= 7
32
+ if ACTIVE_RECORD_VERSION >= V7_0
17
33
  ActiveRecord.default_timezone = :utc
18
34
  else
19
35
  ActiveRecord::Base.default_timezone = :utc
@@ -74,7 +90,14 @@ module ArelExtensions
74
90
  @justin = User.where(id: u.id)
75
91
  u = User.create age: nil, name: 'nilly', created_at: nil, score: nil
76
92
  @nilly = User.where(id: u.id)
77
-
93
+ u = User.create age: nil, name: 'esmé', created_at: nil, score: nil
94
+ @esme = User.where(id: u.id)
95
+ u = User.create age: nil, name: 'esmé ', created_at: nil, score: nil
96
+ @esme2 = User.where(id: u.id)
97
+ u = User.create age: nil, name: nil, created_at: nil, score: nil
98
+ @all_nil = User.where(id: u.id)
99
+
100
+ @id = User.arel_table[:id]
78
101
  @age = User.arel_table[:age]
79
102
  @name = User.arel_table[:name]
80
103
  @score = User.arel_table[:score]
@@ -96,7 +119,9 @@ module ArelExtensions
96
119
  end
97
120
 
98
121
  def t(scope, node)
99
- scope.select(node.as('res')).to_a.first.res
122
+ res = scope.select(node.as('res'))
123
+ # puts "[scope] #{res.to_sql}"
124
+ res.to_a.first.res
100
125
  end
101
126
 
102
127
  # manage the difference between adapters that handle or not json type
@@ -150,7 +175,7 @@ module ArelExtensions
150
175
  def test_rand
151
176
  assert 42 != User.select(Arel.rand.as('res')).first.res
152
177
  assert 0 <= User.select(Arel.rand.abs.as('res')).first.res
153
- assert_equal 10, User.order(Arel.rand).limit(50).count
178
+ assert_equal 13, User.order(Arel.rand).limit(50).count
154
179
  end
155
180
 
156
181
  def test_round
@@ -253,6 +278,20 @@ module ArelExtensions
253
278
  assert_equal 7, t(@camille, @name.length)
254
279
  assert_equal 7, t(@camille, @name.length.round.abs)
255
280
  assert_equal 42, t(@laure, @name.length + 37)
281
+
282
+ if @env_db == 'mssql'
283
+ # By default it's UTF-16, and configuring the CI to be UTF-8 is a bit of a hassle.
284
+ assert_equal 4, t(@esme, @name.byte_size)
285
+ assert_equal 5, t(@esme2, @name.byte_size)
286
+ else
287
+ assert_equal 5, t(@esme, @name.byte_size)
288
+ assert_equal 6, t(@esme2, @name.byte_size)
289
+ end
290
+ assert_equal 0, t(@all_nil, @name.byte_size)
291
+
292
+ assert_equal 4, t(@esme, @name.char_length)
293
+ assert_equal 5, t(@esme2, @name.char_length)
294
+ assert_equal 0, t(@all_nil, @name.char_length)
256
295
  end
257
296
 
258
297
  def test_md5
@@ -341,9 +380,9 @@ module ArelExtensions
341
380
  skip "Sqlite version can't load extension for regexp" if $sqlite && $load_extension_disabled
342
381
  skip 'SQL Server does not know about REGEXP without extensions' if @env_db == 'mssql'
343
382
  assert_equal 1, User.where(@name =~ '^M').count
344
- assert_equal 8, User.where(@name !~ '^L').count
383
+ assert_equal 10, User.where(@name !~ '^L').count
345
384
  assert_equal 1, User.where(@name =~ /^M/).count
346
- assert_equal 8, User.where(@name !~ /^L/).count
385
+ assert_equal 10, User.where(@name !~ /^L/).count
347
386
  end
348
387
 
349
388
  def test_regex_matches
@@ -356,8 +395,12 @@ module ArelExtensions
356
395
  def test_imatches
357
396
  # puts User.where(@name.imatches('m%')).to_sql
358
397
  assert_equal 1, User.where(@name.imatches('m%')).count
359
- assert_equal 4, User.where(@name.imatches_any(['L%', '%e'])).count
360
- assert_equal 8, User.where(@name.idoes_not_match('L%')).count
398
+ if @env_db == 'mysql'
399
+ assert_equal 5, User.where(@name.imatches_any(['L%', '%e'])).count
400
+ else
401
+ assert_equal 4, User.where(@name.imatches_any(['L%', '%e'])).count
402
+ end
403
+ assert_equal 10, User.where(@name.idoes_not_match('L%')).count
361
404
  end
362
405
 
363
406
  def test_replace
@@ -382,8 +425,8 @@ module ArelExtensions
382
425
  skip "Sqlite version can't load extension for soundex" if $sqlite && $load_extension_disabled
383
426
  skip "PostgreSql version can't load extension for soundex" if @env_db == 'postgresql'
384
427
  assert_equal 'C540', t(@camille, @name.soundex)
385
- assert_equal 10, User.where(@name.soundex.eq(@name.soundex)).count
386
- assert_equal 10, User.where(@name.soundex == @name.soundex).count
428
+ assert_equal 12, User.where(@name.soundex.eq(@name.soundex)).count
429
+ assert_equal 12, User.where(@name.soundex == @name.soundex).count
387
430
  end
388
431
 
389
432
  def test_change_case
@@ -680,7 +723,7 @@ module ArelExtensions
680
723
  def test_date_comparator
681
724
  d = Date.new(2016, 5, 23)
682
725
  assert_equal 0, User.where(@created_at < d).count
683
- assert_equal 10, User.where(@created_at >= d).count
726
+ assert_equal 13, User.where(@created_at >= d).count
684
727
  end
685
728
 
686
729
  def test_date_duration
@@ -863,7 +906,7 @@ module ArelExtensions
863
906
  def test_math_minus
864
907
  d = Date.new(2016, 5, 20)
865
908
  # Datediff
866
- assert_equal 10, User.where((@created_at - @created_at).eq(0)).count
909
+ assert_equal 13, User.where((@created_at - @created_at).eq(0)).count
867
910
  assert_equal 3, @laure.select((@created_at - d).as('res')).first.res.abs.to_i
868
911
  # Substraction
869
912
  assert_equal 0, User.where((@age - 10).eq(50)).count
@@ -982,10 +1025,15 @@ module ArelExtensions
982
1025
  end
983
1026
 
984
1027
  def test_subquery_with_order
985
- skip if ['mssql'].include?(@env_db) && Arel::VERSION.to_i < 10
986
- assert_equal 10, User.where(name: User.select(:name).order(:name)).count
987
- assert_equal 10, User.where(@ut[:name].in(@ut.project(@ut[:name]).order(@ut[:name]))).count
988
- if !['mysql'].include?(@env_db) # MySql can't have limit in IN subquery
1028
+ skip if @env_db == 'mssql' && Arel::VERSION.to_i < 10
1029
+ assert_equal 12, User.where(name: User.select(:name).order(:name)).count
1030
+ assert_equal 12, User.where(@ut[:name].in(@ut.project(@ut[:name]).order(@ut[:name]))).count
1031
+
1032
+ if %w[mssql sqlite].include? @env_db
1033
+ # Sqlite and mssql are sensistive to the nil value in name, defined by @all_nil
1034
+ assert_equal 1, User.where(name: User.select(:name).order(:name).limit(2)).count
1035
+ elsif @env_db != 'mysql'
1036
+ # MySql can't have limit in IN subquery
989
1037
  assert_equal 2, User.where(name: User.select(:name).order(:name).limit(2)).count
990
1038
  # assert_equal 6, User.where(name: User.select(:name).order(:name).offset(2)).count
991
1039
  end