arel_extensions 2.0.21 → 2.2.2

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -2
  3. data/.github/workflows/publish.yml +29 -0
  4. data/.github/workflows/release.yml +30 -0
  5. data/.github/workflows/ruby.yml +377 -80
  6. data/.gitignore +7 -6
  7. data/.rubocop.yml +62 -1
  8. data/CONTRIBUTING.md +102 -0
  9. data/Gemfile +2 -23
  10. data/NEWS.md +89 -0
  11. data/README.md +228 -84
  12. data/Rakefile +11 -4
  13. data/TODO +0 -1
  14. data/appveyor.yml +60 -22
  15. data/arel_extensions.gemspec +11 -12
  16. data/bin/build +15 -0
  17. data/bin/compose +6 -0
  18. data/bin/publish +8 -0
  19. data/dev/arelx.dockerfile +44 -0
  20. data/dev/compose.yaml +71 -0
  21. data/dev/postgres.dockerfile +5 -0
  22. data/dev/rbenv +189 -0
  23. data/gemfiles/rails3.gemfile +10 -10
  24. data/gemfiles/rails4_2.gemfile +38 -0
  25. data/gemfiles/rails5.gemfile +29 -0
  26. data/gemfiles/rails5_1_4.gemfile +13 -13
  27. data/gemfiles/rails5_2.gemfile +16 -14
  28. data/gemfiles/rails6.gemfile +18 -15
  29. data/gemfiles/rails6_1.gemfile +18 -15
  30. data/gemfiles/rails7.gemfile +33 -0
  31. data/gemfiles/rails7_1.gemfile +33 -0
  32. data/gemfiles/rails7_2.gemfile +33 -0
  33. data/gemspecs/arel_extensions-v1.gemspec +12 -13
  34. data/gemspecs/arel_extensions-v2.gemspec +11 -12
  35. data/init/mssql.sql +0 -0
  36. data/init/mysql.sql +0 -0
  37. data/init/oracle.sql +0 -0
  38. data/init/postgresql.sql +0 -0
  39. data/init/sqlite.sql +0 -0
  40. data/lib/arel_extensions/aliases.rb +14 -0
  41. data/lib/arel_extensions/attributes.rb +10 -2
  42. data/lib/arel_extensions/boolean_functions.rb +2 -4
  43. data/lib/arel_extensions/common_sql_functions.rb +12 -12
  44. data/lib/arel_extensions/comparators.rb +14 -14
  45. data/lib/arel_extensions/date_duration.rb +14 -9
  46. data/lib/arel_extensions/helpers.rb +62 -0
  47. data/lib/arel_extensions/insert_manager.rb +19 -17
  48. data/lib/arel_extensions/math.rb +48 -45
  49. data/lib/arel_extensions/math_functions.rb +18 -18
  50. data/lib/arel_extensions/nodes/abs.rb +0 -0
  51. data/lib/arel_extensions/nodes/aggregate_function.rb +0 -0
  52. data/lib/arel_extensions/nodes/blank.rb +1 -1
  53. data/lib/arel_extensions/nodes/case.rb +10 -12
  54. data/lib/arel_extensions/nodes/cast.rb +6 -6
  55. data/lib/arel_extensions/nodes/ceil.rb +0 -0
  56. data/lib/arel_extensions/nodes/change_case.rb +0 -0
  57. data/lib/arel_extensions/nodes/coalesce.rb +1 -1
  58. data/lib/arel_extensions/nodes/collate.rb +9 -9
  59. data/lib/arel_extensions/nodes/concat.rb +2 -2
  60. data/lib/arel_extensions/nodes/date_diff.rb +33 -14
  61. data/lib/arel_extensions/nodes/duration.rb +0 -0
  62. data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
  63. data/lib/arel_extensions/nodes/floor.rb +0 -0
  64. data/lib/arel_extensions/nodes/format.rb +3 -2
  65. data/lib/arel_extensions/nodes/formatted_date.rb +42 -0
  66. data/lib/arel_extensions/nodes/formatted_number.rb +2 -2
  67. data/lib/arel_extensions/nodes/function.rb +22 -26
  68. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  69. data/lib/arel_extensions/nodes/json.rb +15 -9
  70. data/lib/arel_extensions/nodes/length.rb +6 -0
  71. data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
  72. data/lib/arel_extensions/nodes/locate.rb +1 -1
  73. data/lib/arel_extensions/nodes/log10.rb +0 -0
  74. data/lib/arel_extensions/nodes/matches.rb +1 -1
  75. data/lib/arel_extensions/nodes/md5.rb +0 -0
  76. data/lib/arel_extensions/nodes/power.rb +0 -0
  77. data/lib/arel_extensions/nodes/rand.rb +0 -0
  78. data/lib/arel_extensions/nodes/repeat.rb +2 -2
  79. data/lib/arel_extensions/nodes/replace.rb +2 -10
  80. data/lib/arel_extensions/nodes/rollup.rb +36 -0
  81. data/lib/arel_extensions/nodes/round.rb +0 -0
  82. data/lib/arel_extensions/nodes/select.rb +10 -0
  83. data/lib/arel_extensions/nodes/soundex.rb +2 -2
  84. data/lib/arel_extensions/nodes/std.rb +0 -0
  85. data/lib/arel_extensions/nodes/substring.rb +1 -1
  86. data/lib/arel_extensions/nodes/sum.rb +0 -0
  87. data/lib/arel_extensions/nodes/then.rb +1 -1
  88. data/lib/arel_extensions/nodes/trim.rb +2 -2
  89. data/lib/arel_extensions/nodes/union.rb +5 -5
  90. data/lib/arel_extensions/nodes/union_all.rb +4 -4
  91. data/lib/arel_extensions/nodes/wday.rb +0 -0
  92. data/lib/arel_extensions/nodes.rb +0 -0
  93. data/lib/arel_extensions/null_functions.rb +16 -0
  94. data/lib/arel_extensions/predications.rb +10 -10
  95. data/lib/arel_extensions/railtie.rb +1 -1
  96. data/lib/arel_extensions/set_functions.rb +3 -3
  97. data/lib/arel_extensions/string_functions.rb +19 -10
  98. data/lib/arel_extensions/tasks.rb +2 -2
  99. data/lib/arel_extensions/version.rb +1 -1
  100. data/lib/arel_extensions/visitors/convert_format.rb +0 -0
  101. data/lib/arel_extensions/visitors/ibm_db.rb +20 -20
  102. data/lib/arel_extensions/visitors/mssql.rb +394 -169
  103. data/lib/arel_extensions/visitors/mysql.rb +238 -151
  104. data/lib/arel_extensions/visitors/oracle.rb +170 -131
  105. data/lib/arel_extensions/visitors/oracle12.rb +16 -16
  106. data/lib/arel_extensions/visitors/postgresql.rb +170 -140
  107. data/lib/arel_extensions/visitors/sqlite.rb +88 -87
  108. data/lib/arel_extensions/visitors/to_sql.rb +185 -156
  109. data/lib/arel_extensions/visitors.rb +73 -60
  110. data/lib/arel_extensions.rb +173 -36
  111. data/test/arelx_test_helper.rb +49 -1
  112. data/test/database.yml +13 -7
  113. data/test/real_db_test.rb +101 -83
  114. data/test/support/fake_record.rb +8 -2
  115. data/test/test_comparators.rb +5 -5
  116. data/test/visitors/test_bulk_insert_oracle.rb +5 -5
  117. data/test/visitors/test_bulk_insert_sqlite.rb +5 -5
  118. data/test/visitors/test_bulk_insert_to_sql.rb +5 -5
  119. data/test/visitors/test_oracle.rb +14 -14
  120. data/test/visitors/test_to_sql.rb +121 -93
  121. data/test/with_ar/all_agnostic_test.rb +630 -320
  122. data/test/with_ar/insert_agnostic_test.rb +25 -18
  123. data/test/with_ar/test_bulk_sqlite.rb +11 -7
  124. data/test/with_ar/test_math_sqlite.rb +18 -14
  125. data/test/with_ar/test_string_mysql.rb +26 -22
  126. data/test/with_ar/test_string_sqlite.rb +26 -22
  127. data/version_v1.rb +1 -1
  128. data/version_v2.rb +1 -1
  129. metadata +24 -26
  130. data/.travis/oracle/download.js +0 -152
  131. data/.travis/oracle/download.sh +0 -30
  132. data/.travis/oracle/download_ojdbc.js +0 -116
  133. data/.travis/oracle/install.sh +0 -34
  134. data/.travis/setup_accounts.sh +0 -9
  135. data/.travis/sqlite3/extension-functions.sh +0 -6
  136. data/.travis.yml +0 -193
  137. data/gemfiles/rails4.gemfile +0 -29
  138. data/gemfiles/rails5_0.gemfile +0 -29
@@ -1,6 +1,32 @@
1
+ # MSSQL visitors for java and rails ≥ 7 are painful to work with:
2
+ # requiring the exact path to the visitor is needed even if the
3
+ # AR adapter was loaded. It's also needed exactly here because:
4
+ # 1. putting it inside the visitor or anywhere else will not
5
+ # guarantee its actual loading.
6
+ # 2. it needs to load before arel_extensions/visitors.
7
+ if RUBY_PLATFORM == 'java' \
8
+ && RUBY_ENGINE == 'jruby' \
9
+ && (version = JRUBY_VERSION.split('.').map(&:to_i)) && version[0] == 9 && version[1] >= 4 \
10
+ && Gem::Specification.find { |g| g.name == 'jdbc-mssql' }
11
+ begin
12
+ require 'arel/visitors/sqlserver'
13
+ rescue LoadError
14
+ warn 'arel/visitors/sqlserver not found: MSSQL might not work correctly.'
15
+ end
16
+ elsif RUBY_PLATFORM != 'java' \
17
+ && Arel::VERSION.to_i < 10 \
18
+ && Gem::Specification.find { |g| g.name == 'activerecord-sqlserver-adapter' }
19
+ begin
20
+ require 'arel_sqlserver'
21
+ rescue LoadError
22
+ warn 'arel_sqlserver not found: SQLServer Visitor might not work correctly.'
23
+ end
24
+ end
25
+
1
26
  require 'arel_extensions/visitors/convert_format'
2
27
  require 'arel_extensions/visitors/to_sql'
3
28
  require 'arel_extensions/visitors/mysql'
29
+ require 'arel_extensions/visitors/mssql'
4
30
  require 'arel_extensions/visitors/postgresql'
5
31
  require 'arel_extensions/visitors/sqlite'
6
32
 
@@ -9,81 +35,68 @@ if defined?(Arel::Visitors::Oracle)
9
35
  require 'arel_extensions/visitors/oracle12'
10
36
  end
11
37
 
12
- if defined?(Arel::Visitors::MSSQL)
13
- require 'arel_extensions/visitors/mssql'
14
-
15
- class Arel::Visitors::MSSQL
38
+ if defined?(Arel::Visitors::SQLServer)
39
+ class Arel::Visitors::SQLServer
16
40
  include ArelExtensions::Visitors::MSSQL
41
+ end
42
+ end
17
43
 
18
- alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
19
- def visit_Arel_Nodes_As o, collector
20
- if o.left.is_a?(Arel::Nodes::Binary)
21
- collector << '('
22
- collector = visit o.left, collector
23
- collector << ')'
24
- else
25
- collector = visit o.left, collector
26
- end
27
- collector << " AS ["
28
- collector = visit o.right, collector
29
- collector << "]"
30
- collector
44
+ if defined?(Arel::Visitors::DepthFirst)
45
+ class Arel::Visitors::DepthFirst
46
+ def visit_Arel_SelectManager o
47
+ visit o.ast
31
48
  end
49
+ end
50
+ end
32
51
 
33
- alias_method(:old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement) rescue nil
52
+ if defined?(Arel::Visitors::MSSQL)
53
+ class Arel::Visitors::MSSQL
54
+ include ArelExtensions::Visitors::MSSQL
55
+
56
+ alias_method(:old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement)
34
57
  def visit_Arel_Nodes_SelectStatement o, collector
35
58
  if !collector.value.blank? && o.limit.blank? && o.offset.blank?
36
59
  o = o.dup
37
60
  o.orders = []
38
61
  end
39
- old_visit_Arel_Nodes_SelectStatement(o,collector)
62
+ old_visit_Arel_Nodes_SelectStatement(o, collector)
40
63
  end
41
64
  end
65
+ end
42
66
 
43
- begin
44
- require 'arel_sqlserver'
45
- if Arel::VERSION.to_i == 6
46
- if Arel::Visitors::VISITORS['sqlserver'] && Arel::Visitors::VISITORS['sqlserver'] != Arel::Visitors::MSSQL
47
- Arel::Visitors::VISITORS['sqlserver'].class_eval do
48
- include ArelExtensions::Visitors::MSSQL
49
-
50
- alias_method(:old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement) rescue nil
51
- def visit_Arel_Nodes_SelectStatement o, collector
52
- if !collector.value.blank? && o.limit.blank? && o.offset.blank?
53
- o = o.dup
54
- o.orders = []
55
- end
56
- old_visit_Arel_Nodes_SelectStatement(o,collector)
57
- end
58
-
59
- alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
60
- def visit_Arel_Nodes_As o, collector
61
- if o.left.is_a?(Arel::Nodes::Binary)
62
- collector << '('
63
- collector = visit o.left, collector
64
- collector << ')'
65
- else
66
- collector = visit o.left, collector
67
- end
68
- collector << " AS ["
69
- collector = visit o.right, collector
70
- collector << "]"
71
- collector
72
- end
67
+ if defined?(Arel::Visitors::SQLServer)
68
+ class Arel::Visitors::SQLServer
69
+ include ArelExtensions::Visitors::MSSQL
73
70
 
74
- alias_method(:old_primary_Key_From_Table, :primary_Key_From_Table) rescue nil
75
- def primary_Key_From_Table t
76
- return unless t
71
+ # There's a bug when working with jruby 9.4 that prevents us from
72
+ # refactoring this and putting it in the main module, or even in a separate
73
+ # module then including it.
74
+ #
75
+ # Reason: the line in this file that does:
76
+ #
77
+ # require 'arel_extensions/visitors/mssql'
78
+ #
79
+ # The error could be seen by:
80
+ #
81
+ # 1. placing the visit_ inside the visitor, or placing it in a module
82
+ # then including it here.
83
+ # 2. replacing the `rescue nil` from aliasing trick, and printing the
84
+ # error.
85
+ #
86
+ # It complains that the visit_ does not exist in the module, as if it's
87
+ # evaluating the module eagerly, instead of lazily like in other versions
88
+ # of ruby.
89
+ #
90
+ # It might be something different, but this is the first thing we should
91
+ # investigate.
77
92
 
78
- column_name = @connection.schema_cache.primary_keys(t.name) ||
79
- @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
80
- column_name ? t[column_name] : nil
81
- end
82
- end
93
+ alias_method(:old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement) rescue nil
94
+ def visit_Arel_Nodes_SelectStatement o, collector
95
+ if !collector.value.blank? && o.limit.blank? && o.offset.blank?
96
+ o = o.dup
97
+ o.orders = []
83
98
  end
99
+ old_visit_Arel_Nodes_SelectStatement(o, collector)
84
100
  end
85
- rescue LoadError
86
- rescue => e
87
- e
88
101
  end
89
102
  end
@@ -1,4 +1,5 @@
1
1
  require 'arel'
2
+ require 'base64'
2
3
 
3
4
  require 'arel_extensions/railtie' if defined?(Rails::Railtie)
4
5
 
@@ -16,7 +17,7 @@ class Arel::Nodes::Casted
16
17
  include Arel::AliasPredication
17
18
 
18
19
  # They forget to define hash.
19
- if Gem::Version.new(Arel::VERSION) < Gem::Version.new("10.0.0")
20
+ if Gem::Version.new(Arel::VERSION) < Gem::Version.new('10.0.0')
20
21
  def hash
21
22
  [self.class, self.val, self.attribute].hash
22
23
  end
@@ -34,9 +35,9 @@ class Arel::Nodes::Grouping
34
35
  end
35
36
 
36
37
  class Arel::Nodes::Ordering
37
- def eql? other
38
- self.hash.eql? other.hash
39
- end
38
+ def eql? other
39
+ self.hash.eql? other.hash
40
+ end
40
41
  end
41
42
 
42
43
  class Arel::Nodes::Function
@@ -44,14 +45,16 @@ class Arel::Nodes::Function
44
45
  include Arel::Expressions
45
46
  end
46
47
 
47
- if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("7.1.0")
48
+ if Gem::Version.new(Arel::VERSION) >= Gem::Version.new('7.1.0')
48
49
  class Arel::Nodes::Case
49
50
  include Arel::Math
50
51
  include Arel::Expressions
51
52
  end
52
53
  end
53
54
 
55
+ require 'arel_extensions/helpers'
54
56
  require 'arel_extensions/version'
57
+ require 'arel_extensions/aliases'
55
58
  require 'arel_extensions/attributes'
56
59
  require 'arel_extensions/visitors'
57
60
  require 'arel_extensions/nodes'
@@ -75,45 +78,107 @@ require 'arel_extensions/nodes/case'
75
78
  require 'arel_extensions/nodes/soundex'
76
79
  require 'arel_extensions/nodes/cast'
77
80
  require 'arel_extensions/nodes/json'
81
+ require 'arel_extensions/nodes/rollup'
82
+ require 'arel_extensions/nodes/select'
83
+
84
+ # It seems like the code in lib/arel_extensions/visitors.rb that is supposed
85
+ # to inject ArelExtension is not enough. Different versions of the sqlserver
86
+ # adapter behave differently. It doesn't always proc, so we added this for
87
+ # coverage.
88
+ if defined?(Arel::Visitors::SQLServer)
89
+ Arel::Visitors.const_set('MSSQL', Arel::Visitors::SQLServer)
90
+ require 'arel_extensions/visitors/mssql'
91
+ class Arel::Visitors::SQLServer
92
+ include ArelExtensions::Visitors::MSSQL
93
+ end
94
+ end
78
95
 
96
+ module Arel
97
+ def self.column_of table_name, column_name
98
+ ArelExtensions.column_of(table_name, column_name)
99
+ end
79
100
 
101
+ def self.duration s, expr
102
+ ArelExtensions::Nodes::Duration.new("#{s}i", expr)
103
+ end
80
104
 
81
- module Arel
82
- def self.rand
83
- ArelExtensions::Nodes::Rand.new
105
+ # The FALSE pseudo literal.
106
+ def self.false
107
+ Arel::Nodes::Equality.new(1, 0)
84
108
  end
85
109
 
86
- def self.shorten s
87
- Base64.urlsafe_encode64(Digest::MD5.new.digest(s)).tr('=', '').tr('-', '_')
110
+ def self.grouping *v
111
+ Arel::Nodes::Grouping.new(*v)
88
112
  end
89
113
 
90
114
  def self.json *expr
91
- if expr.length == 1
92
- ArelExtensions::Nodes::Json.new(expr.first)
93
- else
94
- ArelExtensions::Nodes::Json.new(expr)
95
- end
115
+ ArelExtensions::Nodes::Json.new(
116
+ if expr.length == 1
117
+ expr.first
118
+ else
119
+ expr
120
+ end
121
+ )
96
122
  end
97
123
 
98
- def self.when condition
99
- ArelExtensions::Nodes::Case.new.when(condition)
124
+ def self.json_true
125
+ res = Arel.grouping(Arel.quoted('true'))
126
+ res.instance_eval {
127
+ def return_type
128
+ :boolean
129
+ end
130
+ }
131
+ res
100
132
  end
101
133
 
102
- def self.duration s, expr
103
- ArelExtensions::Nodes::Duration.new(s.to_s+'i',expr)
134
+ def self.json_false
135
+ res = Arel.grouping(Arel.quoted('false'))
136
+ res.instance_eval {
137
+ def return_type
138
+ :boolean
139
+ end
140
+ }
141
+ res
104
142
  end
105
143
 
106
- def self.true
107
- Arel::Nodes::Equality.new(1,1)
144
+ # The NULL literal.
145
+ def self.null
146
+ Arel.quoted(nil)
108
147
  end
109
148
 
110
- def self.false
111
- Arel::Nodes::Equality.new(1,0)
149
+ def self.quoted *args
150
+ Arel::Nodes.build_quoted(*args)
151
+ end
152
+
153
+ def self.rand
154
+ ArelExtensions::Nodes::Rand.new
155
+ end
156
+
157
+ def self.rollup(*args)
158
+ Arel::Nodes::RollUp.new(args)
159
+ end
160
+
161
+ def self.shorten s
162
+ Base64.urlsafe_encode64(Digest::MD5.new.digest(s)).tr('=', '').tr('-', '_')
163
+ end
164
+
165
+ # The TRUE pseudo literal.
166
+ def self.true
167
+ Arel::Nodes::Equality.new(1, 1)
112
168
  end
113
169
 
114
170
  def self.tuple *v
115
- tmp = Arel::Nodes::Grouping.new(nil)
116
- Arel::Nodes::Grouping.new(v.map{|e| tmp.convert_to_node(e)})
171
+ tmp = Arel.grouping(nil)
172
+ Arel.grouping v.map{|e| tmp.convert_to_node(e)}
173
+ end
174
+
175
+ # For instance
176
+ #
177
+ # ```
178
+ # Arel.when(at[field].is_null).then(0).else(1)
179
+ # ```
180
+ def self.when condition
181
+ ArelExtensions::Nodes::Case.new.when(condition)
117
182
  end
118
183
  end
119
184
 
@@ -123,6 +188,7 @@ class Arel::Attributes::Attribute
123
188
  end
124
189
 
125
190
  class Arel::Nodes::Function
191
+ include ArelExtensions::Aliases
126
192
  include ArelExtensions::Math
127
193
  include ArelExtensions::Comparators
128
194
  include ArelExtensions::DateDuration
@@ -135,7 +201,7 @@ class Arel::Nodes::Function
135
201
  alias_method(:old_as, :as) rescue nil
136
202
  def as other
137
203
  res = Arel::Nodes::As.new(self.clone, Arel.sql(other))
138
- if Gem::Version.new(Arel::VERSION) >= Gem::Version.new("9.0.0")
204
+ if Gem::Version.new(Arel::VERSION) >= Gem::Version.new('9.0.0')
139
205
  self.alias = Arel.sql(other)
140
206
  end
141
207
  res
@@ -143,13 +209,13 @@ class Arel::Nodes::Function
143
209
  end
144
210
 
145
211
  class Arel::Nodes::Grouping
146
- include ArelExtensions::Math
147
- include ArelExtensions::Comparators
148
- include ArelExtensions::DateDuration
149
- include ArelExtensions::MathFunctions
150
- include ArelExtensions::NullFunctions
151
- include ArelExtensions::StringFunctions
152
- include ArelExtensions::Predications
212
+ include ArelExtensions::Math
213
+ include ArelExtensions::Comparators
214
+ include ArelExtensions::DateDuration
215
+ include ArelExtensions::MathFunctions
216
+ include ArelExtensions::NullFunctions
217
+ include ArelExtensions::StringFunctions
218
+ include ArelExtensions::Predications
153
219
  end
154
220
 
155
221
  class Arel::Nodes::Unary
@@ -158,6 +224,9 @@ class Arel::Nodes::Unary
158
224
  include ArelExtensions::MathFunctions
159
225
  include ArelExtensions::Comparators
160
226
  include ArelExtensions::Predications
227
+ def eql? other
228
+ hash == other.hash
229
+ end
161
230
  end
162
231
 
163
232
  class Arel::Nodes::Binary
@@ -167,6 +236,9 @@ class Arel::Nodes::Binary
167
236
  include ArelExtensions::Comparators
168
237
  include ArelExtensions::BooleanFunctions
169
238
  include ArelExtensions::Predications
239
+ def eql? other
240
+ hash == other.hash
241
+ end
170
242
  end
171
243
 
172
244
  class Arel::Nodes::Equality
@@ -183,6 +255,19 @@ end
183
255
  class Arel::SelectManager
184
256
  include ArelExtensions::SetFunctions
185
257
  include ArelExtensions::Nodes
258
+
259
+ def as table_name
260
+ Arel::Nodes::TableAlias.new(self, table_name)
261
+ end
262
+
263
+ # Install an alias, if present.
264
+ def xas table_name
265
+ if table_name.present?
266
+ as table_name
267
+ else
268
+ self
269
+ end
270
+ end
186
271
  end
187
272
 
188
273
  class Arel::Nodes::As
@@ -191,8 +276,19 @@ end
191
276
 
192
277
  class Arel::Table
193
278
  alias_method(:old_alias, :alias) rescue nil
279
+
280
+ # activerecord 7.1 removed the alias. We might need to remove our dependency
281
+ # on the alias if it proves problematic.
282
+ if !self.respond_to?(:table_name)
283
+ alias :table_name :name
284
+ end
285
+
194
286
  def alias(name = "#{self.name}_2")
195
- name.blank? ? self : Arel::Nodes::TableAlias.new(self,name)
287
+ if name.present?
288
+ Arel::Nodes::TableAlias.new(self, name)
289
+ else
290
+ self
291
+ end
196
292
  end
197
293
  end
198
294
 
@@ -200,9 +296,50 @@ class Arel::Nodes::TableAlias
200
296
  def method_missing(*args)
201
297
  met = args.shift.to_sym
202
298
  if self.relation.respond_to?(met)
203
- self.relation.send(met,args)
299
+ self.relation.send(met, args)
204
300
  else
205
- super(met,*args)
301
+ super(met, *args)
302
+ end
303
+ end
304
+ end
305
+
306
+
307
+ class Arel::Attributes::Attribute
308
+ def to_sql(engine = Arel::Table.engine)
309
+ collector = Arel::Collectors::SQLString.new
310
+ collector = engine.connection.visitor.accept self, collector
311
+ collector.value
312
+ end
313
+
314
+ def rollup
315
+ Arel::Nodes::RollUp.new([self])
316
+ end
317
+ end
318
+
319
+ class Arel::Nodes::Node
320
+ def rollup
321
+ Arel::Nodes::RollUp.new([self])
322
+ end
323
+ end
324
+
325
+ require 'active_record'
326
+ if ActiveRecord.version >= Gem::Version.create('7.2')
327
+ class ActiveRecord::Relation::WhereClause
328
+ def except_predicates(columns)
329
+ attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
330
+ non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
331
+
332
+ predicates.reject do |node|
333
+ if !non_attrs.empty? && node.equality? && node.left.is_a?(Arel::Predications)
334
+ non_attrs.include?(node.left)
335
+ end || Arel.fetch_attribute(node) do |attr|
336
+ attrs.find { |v| v.eql? attr } || columns.include?(attr.name.to_s) # 👈 replces
337
+ # attrs.include?(attr) || columns.include?(attr.name.to_s) # 👈 this
338
+ #
339
+ # And that's because our attributes override `==`, so `attrs.include?(attr)` always
340
+ # passes because it returns an equality node.
341
+ end
342
+ end
206
343
  end
207
344
  end
208
345
  end
@@ -6,8 +6,56 @@ require 'active_record'
6
6
 
7
7
  require 'support/fake_record'
8
8
 
9
+ def colored(color, msg)
10
+ /^xterm|-256color$/.match?(ENV['TERM']) ? "\x1b[#{color}m#{msg}\x1b[89m\x1b[0m" : "#{msg}"
11
+ end
12
+
13
+ YELLOW = '33'
14
+
15
+ # Load gems specific to databases.
16
+ #
17
+ # NOTE:
18
+ # It's strongly advised to test each database on its own. Loading multiple
19
+ # backend gems leads to undefined behavior according to tests; the backend
20
+ # might not recognize the correct DB visitor and will fallback to `ToSQL`
21
+ # and screw all tests.
22
+ #
23
+ # The issue also seems to be related to arel version: at some point, arel
24
+ # dropped its wide support for DBs and kept Postgres, MySQL and SQLite.
25
+ # Here, we're just trying to load the correct ones.
26
+ #
27
+ # NOTE:
28
+ # As of jruby 9.4 (and maybe 9.3, but I couldn't test it given the state of
29
+ # the alt-adapter), we need to load jdbc/mssql manually.
30
+ db_and_gem =
31
+ if RUBY_PLATFORM == 'java'
32
+ {
33
+ 'oracle' => ['activerecord-oracle_enhanced-adapter'],
34
+ 'mssql' => ['jdbc/mssql', 'activerecord-jdbcsqlserver-adapter'],
35
+ }
36
+ else
37
+ {
38
+ 'oracle' => ['activerecord-oracle_enhanced-adapter'],
39
+ 'mssql' => ['activerecord-sqlserver-adapter'],
40
+ }
41
+ end
42
+
43
+ def load_lib(gems)
44
+ if gems && (RUBY_PLATFORM == 'java' || Arel::VERSION.to_i > 9)
45
+ gems.each do |gem|
46
+ begin
47
+ require gem
48
+ rescue Exception => e
49
+ warn "Warning: failed to load gem #{gem}. Are you sure it's installed?"
50
+ warn e.message
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ load_lib(db_and_gem[ENV['DB']])
57
+
9
58
  require 'arel_extensions'
10
- Arel::Table.engine = FakeRecord::Base.new
11
59
 
12
60
  $arel_silence_type_casting_deprecation = true
13
61
 
data/test/database.yml CHANGED
@@ -8,24 +8,30 @@ jdbc-sqlite:
8
8
  timeout: 500
9
9
  mysql:
10
10
  adapter: mysql2
11
- database: arext_test
12
- username: travis
11
+ database: arelx_test
12
+ username: root
13
13
  host: 127.0.0.1
14
14
  port: 3306
15
15
  encoding: utf8
16
16
  jdbc-mysql:
17
17
  adapter: jdbcmysql
18
- database: arext_test
19
- username: travis
18
+ database: arelx_test
19
+ username: root
20
20
  encoding: utf8
21
21
  postgresql:
22
22
  adapter: postgresql
23
- database: arext_test
23
+ database: arelx_test
24
24
  username: postgres
25
+ password: secret
26
+ host: 127.0.0.1
27
+ port: 5432
25
28
  jdbc-postgresql:
26
29
  adapter: jdbcpostgresql
27
- database: arext_test
30
+ database: arelx_test
28
31
  username: postgres
32
+ password: secret
33
+ host: 127.0.0.1
34
+ port: 5432
29
35
  oracle:
30
36
  adapter: oracle_enhanced
31
37
  database: xe
@@ -39,7 +45,7 @@ jdbc-oracle:
39
45
  ibm_db:
40
46
  adapter: ibm_db
41
47
  username: travis
42
- database: arext_test
48
+ database: arelx_test
43
49
  mssql:
44
50
  adapter: sqlserver
45
51
  host: localhost