arel_extensions 1.4.0 → 2.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +2 -1
  3. data/.gitignore +6 -7
  4. data/.rubocop.yml +3 -67
  5. data/.travis/oracle/download.js +152 -0
  6. data/.travis/oracle/download.sh +30 -0
  7. data/.travis/oracle/download_ojdbc.js +116 -0
  8. data/.travis/oracle/install.sh +34 -0
  9. data/.travis/setup_accounts.sh +9 -0
  10. data/.travis/sqlite3/extension-functions.sh +6 -0
  11. data/.travis.yml +223 -0
  12. data/Gemfile +28 -2
  13. data/README.md +90 -239
  14. data/Rakefile +30 -48
  15. data/TODO +1 -0
  16. data/appveyor.yml +22 -60
  17. data/arel_extensions.gemspec +14 -13
  18. data/functions.html +3 -3
  19. data/gemfiles/rails3.gemfile +10 -10
  20. data/gemfiles/rails4.gemfile +29 -0
  21. data/gemfiles/rails5_0.gemfile +29 -0
  22. data/gemfiles/rails5_1_4.gemfile +14 -14
  23. data/gemfiles/rails5_2.gemfile +14 -16
  24. data/init/mssql.sql +4 -4
  25. data/init/mysql.sql +38 -38
  26. data/init/oracle.sql +0 -0
  27. data/init/postgresql.sql +21 -21
  28. data/init/sqlite.sql +0 -0
  29. data/lib/arel_extensions/attributes.rb +3 -15
  30. data/lib/arel_extensions/boolean_functions.rb +14 -53
  31. data/lib/arel_extensions/common_sql_functions.rb +17 -16
  32. data/lib/arel_extensions/comparators.rb +28 -27
  33. data/lib/arel_extensions/date_duration.rb +13 -17
  34. data/lib/arel_extensions/insert_manager.rb +15 -18
  35. data/lib/arel_extensions/math.rb +53 -55
  36. data/lib/arel_extensions/math_functions.rb +39 -46
  37. data/lib/arel_extensions/nodes/abs.rb +1 -0
  38. data/lib/arel_extensions/nodes/blank.rb +2 -1
  39. data/lib/arel_extensions/nodes/case.rb +19 -20
  40. data/lib/arel_extensions/nodes/cast.rb +8 -10
  41. data/lib/arel_extensions/nodes/ceil.rb +1 -1
  42. data/lib/arel_extensions/nodes/coalesce.rb +4 -3
  43. data/lib/arel_extensions/nodes/collate.rb +10 -9
  44. data/lib/arel_extensions/nodes/concat.rb +18 -9
  45. data/lib/arel_extensions/nodes/date_diff.rb +26 -42
  46. data/lib/arel_extensions/nodes/duration.rb +3 -0
  47. data/lib/arel_extensions/nodes/find_in_set.rb +1 -0
  48. data/lib/arel_extensions/nodes/floor.rb +1 -1
  49. data/lib/arel_extensions/nodes/format.rb +8 -35
  50. data/lib/arel_extensions/nodes/formatted_number.rb +23 -22
  51. data/lib/arel_extensions/nodes/function.rb +37 -42
  52. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  53. data/lib/arel_extensions/nodes/json.rb +39 -52
  54. data/lib/arel_extensions/nodes/length.rb +0 -5
  55. data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
  56. data/lib/arel_extensions/nodes/locate.rb +2 -1
  57. data/lib/arel_extensions/nodes/log10.rb +2 -1
  58. data/lib/arel_extensions/nodes/matches.rb +7 -5
  59. data/lib/arel_extensions/nodes/md5.rb +1 -0
  60. data/lib/arel_extensions/nodes/power.rb +5 -5
  61. data/lib/arel_extensions/nodes/rand.rb +1 -0
  62. data/lib/arel_extensions/nodes/repeat.rb +5 -3
  63. data/lib/arel_extensions/nodes/replace.rb +8 -16
  64. data/lib/arel_extensions/nodes/round.rb +6 -5
  65. data/lib/arel_extensions/nodes/soundex.rb +15 -15
  66. data/lib/arel_extensions/nodes/std.rb +21 -18
  67. data/lib/arel_extensions/nodes/substring.rb +16 -8
  68. data/lib/arel_extensions/nodes/then.rb +1 -1
  69. data/lib/arel_extensions/nodes/trim.rb +6 -4
  70. data/lib/arel_extensions/nodes/union.rb +8 -5
  71. data/lib/arel_extensions/nodes/union_all.rb +7 -4
  72. data/lib/arel_extensions/nodes/wday.rb +4 -0
  73. data/lib/arel_extensions/nodes.rb +1 -1
  74. data/lib/arel_extensions/null_functions.rb +5 -19
  75. data/lib/arel_extensions/predications.rb +43 -44
  76. data/lib/arel_extensions/railtie.rb +5 -5
  77. data/lib/arel_extensions/set_functions.rb +7 -5
  78. data/lib/arel_extensions/string_functions.rb +29 -59
  79. data/lib/arel_extensions/tasks.rb +6 -6
  80. data/lib/arel_extensions/version.rb +1 -1
  81. data/lib/arel_extensions/visitors/ibm_db.rb +31 -24
  82. data/lib/arel_extensions/visitors/mssql.rb +191 -422
  83. data/lib/arel_extensions/visitors/mysql.rb +212 -350
  84. data/lib/arel_extensions/visitors/oracle.rb +178 -220
  85. data/lib/arel_extensions/visitors/oracle12.rb +31 -18
  86. data/lib/arel_extensions/visitors/postgresql.rb +173 -256
  87. data/lib/arel_extensions/visitors/sqlite.rb +126 -140
  88. data/lib/arel_extensions/visitors/to_sql.rb +237 -298
  89. data/lib/arel_extensions/visitors.rb +62 -83
  90. data/lib/arel_extensions.rb +31 -224
  91. data/test/database.yml +7 -15
  92. data/test/helper.rb +18 -0
  93. data/test/real_db_test.rb +117 -120
  94. data/test/support/fake_record.rb +3 -9
  95. data/test/test_comparators.rb +17 -14
  96. data/test/visitors/test_bulk_insert_oracle.rb +11 -11
  97. data/test/visitors/test_bulk_insert_sqlite.rb +13 -12
  98. data/test/visitors/test_bulk_insert_to_sql.rb +13 -11
  99. data/test/visitors/test_oracle.rb +55 -55
  100. data/test/visitors/test_to_sql.rb +226 -419
  101. data/test/with_ar/all_agnostic_test.rb +366 -709
  102. data/test/with_ar/insert_agnostic_test.rb +21 -27
  103. data/test/with_ar/test_bulk_sqlite.rb +16 -17
  104. data/test/with_ar/test_math_sqlite.rb +26 -26
  105. data/test/with_ar/test_string_mysql.rb +33 -31
  106. data/test/with_ar/test_string_sqlite.rb +34 -30
  107. metadata +34 -43
  108. data/.github/workflows/publish.yml +0 -29
  109. data/.github/workflows/release.yml +0 -30
  110. data/.github/workflows/ruby.yml +0 -399
  111. data/CONTRIBUTING.md +0 -102
  112. data/NEWS.md +0 -78
  113. data/bin/build +0 -15
  114. data/bin/compose +0 -6
  115. data/bin/publish +0 -8
  116. data/dev/arelx.dockerfile +0 -44
  117. data/dev/compose.yaml +0 -71
  118. data/dev/postgres.dockerfile +0 -5
  119. data/dev/rbenv +0 -189
  120. data/gemfiles/rails4_2.gemfile +0 -38
  121. data/gemfiles/rails5.gemfile +0 -29
  122. data/gemfiles/rails6.gemfile +0 -33
  123. data/gemfiles/rails6_1.gemfile +0 -33
  124. data/gemfiles/rails7.gemfile +0 -33
  125. data/gemfiles/rails7_1.gemfile +0 -33
  126. data/gemfiles/rails7_2.gemfile +0 -33
  127. data/gemspecs/arel_extensions-v1.gemspec +0 -27
  128. data/gemspecs/arel_extensions-v2.gemspec +0 -27
  129. data/generate_gems.sh +0 -15
  130. data/lib/arel_extensions/aliases.rb +0 -14
  131. data/lib/arel_extensions/helpers.rb +0 -62
  132. data/lib/arel_extensions/nodes/aggregate_function.rb +0 -13
  133. data/lib/arel_extensions/nodes/formatted_date.rb +0 -42
  134. data/lib/arel_extensions/nodes/rollup.rb +0 -36
  135. data/lib/arel_extensions/nodes/select.rb +0 -10
  136. data/lib/arel_extensions/nodes/sum.rb +0 -7
  137. data/lib/arel_extensions/visitors/convert_format.rb +0 -37
  138. data/test/arelx_test_helper.rb +0 -74
  139. data/version_v1.rb +0 -3
  140. data/version_v2.rb +0 -3
@@ -1,88 +1,38 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
- class Arel::Visitors::MySQL
4
- DATE_MAPPING = {
5
- 'd' => 'DAY', 'm' => 'MONTH', 'w' => 'WEEK', 'y' => 'YEAR', 'wd' => 'WEEKDAY',
6
- 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'
7
- }.freeze
8
-
9
- DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
3
+ Arel::Visitors::MySQL.class_eval do
4
+ Arel::Visitors::MySQL::COMMA = ", "
5
+ Arel::Visitors::MySQL::DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'WEEK', 'y' => 'YEAR', 'wd' => 'WEEKDAY', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
6
+ Arel::Visitors::MySQL::DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
10
7
  '%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
11
- '%V' => '%v', '%G' => '%x', # ISO week number and year of week
12
- '%d' => '%d', '%e' => '%e', '%j' => '%j', '%w' => '%w', '%a' => '%a', '%A' => '%W', # day, weekday
8
+ '%d' => '%d', '%e' => '%e', '%j' => '%j', '%w' => '%w', '%A' => '%W', # day, weekday
13
9
  '%H' => '%H', '%k' => '%k', '%I' => '%I', '%l' => '%l', '%P' => '%p', '%p' => '%p', # hours
14
10
  '%M' => '%i', '%S' => '%S', '%L' => '', '%N' => '%f', '%z' => ''
15
- }.freeze
16
-
17
- # This helper method did not exist in rails < 5.2
18
- if !Arel::Visitors::MySQL.method_defined?(:collect_nodes_for)
19
- def collect_nodes_for(nodes, collector, spacer, connector = ", ")
20
- if nodes&.any?
21
- collector << spacer
22
- inject_join nodes, collector, connector
23
- end
24
- end
25
- end
26
-
27
- # The whole purpose of this override is to fix the behavior of RollUp.
28
- # All other databases treat RollUp sanely, execpt MySQL which requires
29
- # that it figures as the last element of a GROUP BY.
30
- def visit_Arel_Nodes_SelectCore(o, collector)
31
- collector << "SELECT"
11
+ }
32
12
 
33
- collector = collect_optimizer_hints(o, collector) if self.respond_to?(:collect_optimizer_hinsts)
34
- collector = maybe_visit o.set_quantifier, collector
35
13
 
36
- collect_nodes_for o.projections, collector, " "
37
-
38
- if o.source && !o.source.empty?
39
- collector << " FROM "
40
- collector = visit o.source, collector
41
- end
42
-
43
- # The actual work
44
- groups = o.groups
45
- rollup = groups.select { |g| g.expr.class == Arel::Nodes::RollUp }.map { |r| r.expr.value }
46
- if rollup && !rollup.empty?
47
- groups = o.groups.reject { |g| g.expr.class == Arel::Nodes::RollUp }
48
- groups << Arel::Nodes::RollUp.new(rollup)
49
- end
50
- # FIN
51
-
52
- collect_nodes_for o.wheres, collector, " WHERE ", " AND "
53
- collect_nodes_for groups, collector, " GROUP BY " # Look ma, I'm viring a group
54
- collect_nodes_for o.havings, collector, " HAVING ", " AND "
55
- collect_nodes_for o.windows, collector, " WINDOW "
56
-
57
- if o.respond_to?(:comment)
58
- maybe_visit o.comment, collector
59
- else
60
- collector
61
- end
62
- end
63
-
64
- # Math functions
14
+ #Math functions
65
15
  def visit_ArelExtensions_Nodes_Log10 o, collector
66
- collector << 'LOG10('
67
- o.expressions.each_with_index { |arg, i|
68
- collector << Arel::Visitors::ToSql::COMMA if i != 0
69
- collector = visit arg, collector
70
- }
71
- collector << ')'
72
- collector
16
+ collector << "LOG10("
17
+ o.expressions.each_with_index { |arg, i|
18
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
19
+ collector = visit arg, collector
20
+ }
21
+ collector << ")"
22
+ collector
73
23
  end
74
24
 
75
25
  def visit_ArelExtensions_Nodes_Power o, collector
76
- collector << 'POW('
26
+ collector << "POW("
77
27
  o.expressions.each_with_index { |arg, i|
78
- collector << Arel::Visitors::ToSql::COMMA if i != 0
28
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
79
29
  collector = visit arg, collector
80
30
  }
81
- collector << ')'
31
+ collector << ")"
82
32
  collector
83
33
  end
84
34
 
85
- # String functions
35
+ #String functions
86
36
  def visit_ArelExtensions_Nodes_IMatches o, collector # insensitive on ASCII
87
37
  collector << 'LOWER('
88
38
  collector = visit o.left, collector
@@ -91,9 +41,10 @@ module ArelExtensions
91
41
  collector << ')'
92
42
  if o.escape
93
43
  collector << ' ESCAPE '
94
- collector = visit o.escape, collector
44
+ visit o.escape, collector
45
+ else
46
+ collector
95
47
  end
96
- collector
97
48
  end
98
49
 
99
50
  def visit_ArelExtensions_Nodes_AiMatches o, collector
@@ -102,9 +53,10 @@ module ArelExtensions
102
53
  collector = visit o.right.ai_collate, collector
103
54
  if o.escape
104
55
  collector << ' ESCAPE '
105
- collector = visit o.escape, collector
56
+ visit o.escape, collector
57
+ else
58
+ collector
106
59
  end
107
- collector
108
60
  end
109
61
 
110
62
  def visit_ArelExtensions_Nodes_AiIMatches o, collector
@@ -113,20 +65,22 @@ module ArelExtensions
113
65
  collector = visit o.right.ai_collate, collector
114
66
  if o.escape
115
67
  collector << ' ESCAPE '
116
- collector = visit o.escape, collector
68
+ visit o.escape, collector
69
+ else
70
+ collector
117
71
  end
118
- collector
119
72
  end
120
73
 
121
74
  def visit_ArelExtensions_Nodes_SMatches o, collector
122
- collector = visit o.left.collate, collector
123
- collector << ' LIKE '
124
- collector = visit o.right.collate, collector
125
- if o.escape
126
- collector << ' ESCAPE '
127
- collector = visit o.escape, collector
128
- end
129
- collector
75
+ collector = visit o.left.collate, collector
76
+ collector << ' LIKE '
77
+ collector = visit o.right.collate, collector
78
+ if o.escape
79
+ collector << ' ESCAPE '
80
+ visit o.escape, collector
81
+ else
82
+ collector
83
+ end
130
84
  end
131
85
 
132
86
  def visit_ArelExtensions_Nodes_IDoesNotMatch o, collector
@@ -137,186 +91,142 @@ module ArelExtensions
137
91
  collector << ')'
138
92
  if o.escape
139
93
  collector << ' ESCAPE '
140
- collector = visit o.escape, collector
94
+ visit o.escape, collector
95
+ else
96
+ collector
141
97
  end
142
- collector
143
98
  end
144
99
 
145
100
  def visit_ArelExtensions_Nodes_Collate o, collector
146
- charset =
147
- case o.expressions.first
148
- when Arel::Attributes::Attribute
149
- case o.option
150
- when 'latin1', 'utf8'
101
+ case o.expressions.first
102
+ when Arel::Attributes::Attribute
103
+ charset = case o.option
104
+ when 'latin1','utf8'
151
105
  o.option
152
106
  else
153
107
  Arel::Table.engine.connection.charset || 'utf8'
154
108
  end
155
- else
156
- (o.option == 'latin1') ? 'latin1' : 'utf8'
157
- end
109
+ else
110
+ charset = (o.option == 'latin1') ? 'latin1' : 'utf8'
111
+ end
158
112
  collector = visit o.expressions.first, collector
159
- collector <<
160
- if o.ai
161
- " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : "#{charset}_unicode_ci"}"
162
- # doesn't work in latin1
163
- elsif o.ci
164
- " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : "#{charset}_unicode_ci"}"
165
- else
166
- " COLLATE #{charset}_bin"
167
- end
113
+ if o.ai
114
+ collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
115
+ #doesn't work in latin1
116
+ elsif o.ci
117
+ collector << " COLLATE #{charset == 'latin1' ? 'latin1_general_ci' : 'utf8_unicode_ci' }"
118
+ else
119
+ collector << " COLLATE #{charset}_bin"
120
+ end
168
121
  collector
169
122
  end
170
123
 
171
124
  def visit_ArelExtensions_Nodes_Concat o, collector
172
- collector << 'CONCAT('
125
+ collector << "CONCAT("
173
126
  o.expressions.each_with_index { |arg, i|
174
- collector << COMMA if i != 0
175
- if arg.is_a?(Numeric) || arg.is_a?(Arel::Attributes::Attribute)
176
- collector << 'CAST('
127
+ collector << Arel::Visitors::MySQL::COMMA unless i == 0
128
+ if (arg.is_a?(Numeric)) || (arg.is_a?(Arel::Attributes::Attribute))
129
+ collector << "CAST("
177
130
  collector = visit arg, collector
178
- collector << ' AS char)'
131
+ collector << " AS char)"
179
132
  else
180
133
  collector = visit arg, collector
181
134
  end
182
135
  }
183
- collector << ')'
136
+ collector << ")"
184
137
  collector
185
138
  end
186
139
 
187
- def visit_Arel_Nodes_RollUp(o, collector)
188
- visit o.expr, collector
189
- collector << " WITH ROLLUP"
190
- end
191
-
192
140
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
193
- collector << 'GROUP_CONCAT('
141
+ collector << "GROUP_CONCAT("
194
142
  collector = visit o.left, collector
195
- if !o.order.blank?
143
+ if !o.orders.blank?
196
144
  collector << ' ORDER BY '
197
- o.order.each_with_index do |order, i|
198
- collector << Arel::Visitors::ToSql::COMMA if i != 0
145
+ o.orders.each_with_index do |order,i|
146
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
199
147
  collector = visit order, collector
200
148
  end
201
149
  end
202
- if o.separator && o.separator != 'NULL'
150
+ if o.right && o.right != 'NULL'
203
151
  collector << ' SEPARATOR '
204
- collector = visit o.separator, collector
152
+ collector = visit o.right, collector
205
153
  end
206
- collector << ')'
154
+ collector << ")"
207
155
  collector
208
156
  end
209
157
 
210
158
  def visit_ArelExtensions_Nodes_Trim o, collector
211
- collector << 'TRIM(' # BOTH
212
- collector = visit o.right, collector
213
- collector << ' FROM '
214
- collector = visit o.left, collector
215
- collector << ')'
216
- collector
159
+ collector << 'TRIM(' # BOTH
160
+ collector = visit o.right, collector
161
+ collector << " FROM "
162
+ collector = visit o.left, collector
163
+ collector << ")"
164
+ collector
217
165
  end
218
166
 
219
- def visit_ArelExtensions_Nodes_Ltrim o, collector
220
- collector << 'TRIM(LEADING '
221
- collector = visit o.right, collector
222
- collector << ' FROM '
223
- collector = visit o.left, collector
224
- collector << ')'
225
- collector
167
+ def visit_ArelExtensions_Nodes_Ltrim o , collector
168
+ collector << 'TRIM(LEADING '
169
+ collector = visit o.right, collector
170
+ collector << " FROM "
171
+ collector = visit o.left, collector
172
+ collector << ")"
173
+ collector
226
174
  end
227
175
 
228
- def visit_ArelExtensions_Nodes_Rtrim o, collector
176
+ def visit_ArelExtensions_Nodes_Rtrim o , collector
229
177
  collector << 'TRIM(TRAILING '
230
178
  collector = visit o.right, collector
231
- collector << ' FROM '
179
+ collector << " FROM "
232
180
  collector = visit o.left, collector
233
- collector << ')'
181
+ collector << ")"
234
182
  collector
235
183
  end
236
184
 
237
185
  def visit_ArelExtensions_Nodes_Repeat o, collector
238
- collector << 'REPEAT('
186
+ collector << "REPEAT("
239
187
  o.expressions.each_with_index { |arg, i|
240
- collector << Arel::Visitors::ToSql::COMMA if i != 0
188
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
241
189
  collector = visit arg, collector
242
190
  }
243
- collector << ')'
191
+ collector << ")"
244
192
  collector
245
193
  end
246
194
 
247
- def visit_ArelExtensions_Nodes_RegexpReplace o, collector
248
- if !regexp_replace_supported?
249
- warn('Warning: ArelExtensions: REGEXP_REPLACE does not seem to be available in the current version of the DBMS, it might crash')
250
- end
251
- super(o, collector)
252
- end
253
-
254
195
  def visit_ArelExtensions_Nodes_Format o, collector
255
- # One use case we met is
256
- # `case…when…then(valid_date).else(Arel.null).format(…)`.
257
- #
258
- # In this case, `o.col_type` is `nil` but we have a legitimate type in
259
- # the expression to be formatted. The following is a best effort to
260
- # infer the proper type.
261
- first = o.expressions[0]
262
- type =
263
- o.col_type.nil? \
264
- && (first.respond_to?(:return_type) && !first&.return_type.nil?) \
265
- ? first&.return_type \
266
- : o.col_type
267
-
268
- case type
269
- when :date, :datetime, :time
270
- visit_ArelExtensions_Nodes_FormattedDate o, collector
196
+ case o.col_type
197
+ when :date, :datetime
198
+ collector << "DATE_FORMAT("
199
+ collector = visit o.left, collector
200
+ collector << Arel::Visitors::MySQL::COMMA
201
+ f = o.iso_format.dup
202
+ Arel::Visitors::MySQL::DATE_FORMAT_DIRECTIVES.each { |d, r| f.gsub!(d, r) }
203
+ collector = visit Arel::Nodes.build_quoted(f), collector
204
+ collector << ")"
271
205
  when :integer, :float, :decimal
272
- collector << 'FORMAT('
206
+ collector << "FORMAT("
273
207
  collector = visit o.left, collector
274
208
  collector << Arel::Visitors::ToSql::COMMA
275
209
  collector << '2'
276
210
  collector << Arel::Visitors::ToSql::COMMA
277
211
  collector = visit o.right, collector
278
- collector << ')'
212
+ collector << ")"
279
213
  else
280
214
  collector = visit o.left, collector
281
215
  end
282
216
  collector
283
217
  end
284
218
 
285
- def visit_ArelExtensions_Nodes_FormattedDate o, collector
286
- fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
287
- collector << 'DATE_FORMAT('
288
- collector << 'CONVERT_TZ(' if o.time_zone
289
- collector = visit o.left, collector
290
- case o.time_zone
291
- when Hash
292
- src_tz, dst_tz = o.time_zone.first
293
- collector << COMMA
294
- collector = visit Arel.quoted(src_tz), collector
295
- collector << COMMA
296
- collector = visit Arel.quoted(dst_tz), collector
297
- collector << ')'
298
- when String
299
- collector << COMMA << "'UTC'" << COMMA
300
- collector = visit Arel.quoted(o.time_zone), collector
301
- collector << ')'
302
- end
303
- collector << COMMA
304
- collector = visit Arel.quoted(fmt), collector
305
- collector << ')'
306
- end
307
-
308
219
  def visit_ArelExtensions_Nodes_DateDiff o, collector
309
- case o.right_node_type
310
- when :ruby_date, :ruby_time, :date, :datetime, :time
311
- collector <<
312
- case o.left_node_type
313
- when :ruby_time, :datetime, :time then 'TIMESTAMPDIFF(SECOND, '
314
- else 'DATEDIFF('
315
- end
220
+ if o.right_node_type == :ruby_date || o.right_node_type == :ruby_time || o.right_node_type == :date || o.right_node_type == :datetime || o.right_node_type == :time
221
+ collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
222
+ 'TIMESTAMPDIFF(SECOND, '
223
+ else
224
+ 'DATEDIFF('
225
+ end
316
226
  collector = visit o.right, collector
317
- collector << COMMA
227
+ collector << Arel::Visitors::MySQL::COMMA
318
228
  collector = visit o.left, collector
319
- collector << ')'
229
+ collector << ")"
320
230
  else
321
231
  collector << '('
322
232
  collector = visit o.left, collector
@@ -336,92 +246,101 @@ module ArelExtensions
336
246
  end
337
247
 
338
248
  def visit_ArelExtensions_Nodes_DateAdd o, collector
339
- collector << 'DATE_ADD('
249
+ collector << "DATE_ADD("
340
250
  collector = visit o.left, collector
341
- collector << COMMA
251
+ collector << Arel::Visitors::MySQL::COMMA
342
252
  collector = visit o.mysql_value(o.right), collector
343
- collector << ')'
253
+ collector << ")"
344
254
  collector
345
255
  end
346
256
 
257
+
347
258
  def visit_ArelExtensions_Nodes_Duration o, collector
348
259
  if o.left == 'wd'
349
- collector << '(WEEKDAY('
260
+ collector << "(WEEKDAY("
350
261
  collector = visit o.right, collector
351
- collector << ') + 1) % 7'
262
+ collector << ") + 1) % 7"
352
263
  else
353
264
  if o.with_interval
354
- interval =
355
- case o.left
356
- when 'd', 'm', 'y'
357
- 'DAY'
358
- when 'h', 'mn', 's'
359
- 'SECOND'
360
- when /i\z/
361
- DATE_MAPPING[o.left[0..-2]]
362
- end
265
+ case o.left
266
+ when 'd','m','y'
267
+ interval = 'DAY'
268
+ when 'h','mn','s'
269
+ interval = 'SECOND'
270
+ when /i\z/
271
+ interval = Arel::Visitors::MySQL::DATE_MAPPING[o.left[0..-2]]
272
+ else
273
+ interval = nil
274
+ end
363
275
  end
364
- collector << ' INTERVAL ' if o.with_interval && interval
365
- collector << "#{DATE_MAPPING[o.left]}("
276
+ collector << " INTERVAL " if o.with_interval && interval
277
+ collector << "#{Arel::Visitors::MySQL::DATE_MAPPING[o.left]}("
366
278
  collector = visit o.right, collector
367
- collector << ')'
279
+ collector << ")"
368
280
  collector << " #{interval} " if o.with_interval && interval
369
281
  end
370
282
  collector
371
283
  end
372
284
 
285
+
373
286
  def visit_ArelExtensions_Nodes_IsNull o, collector
374
- collector << 'ISNULL('
287
+ collector << "ISNULL("
375
288
  collector = visit o.expr, collector
376
- collector << ')'
289
+ collector << ")"
377
290
  collector
378
291
  end
379
292
 
380
293
  def visit_ArelExtensions_Nodes_IsNotNull o, collector
381
- collector << 'NOT ISNULL('
294
+ collector << "NOT ISNULL("
382
295
  collector = visit o.expr, collector
383
- collector << ')'
296
+ collector << ")"
384
297
  collector
385
298
  end
386
299
 
387
300
  def visit_ArelExtensions_Nodes_Wday o, collector
388
- collector << '(WEEKDAY('
301
+ collector << "(WEEKDAY("
389
302
  collector = visit o.date, collector
390
- collector << ') + 1) % 7'
303
+ collector << ") + 1) % 7"
391
304
  collector
392
305
  end
393
306
 
394
307
  def visit_ArelExtensions_Nodes_Cast o, collector
395
- as_attr =
396
- case o.as_attr
397
- when :binary then 'binary'
398
- when :date then 'date'
399
- when :datetime then 'datetime'
400
- when :int then 'signed'
401
- when :number, :decimal then 'decimal(20,6)'
402
- when :string then 'char'
403
- when :time then 'time'
404
- else o.as_attr.to_s
405
- end
406
-
407
- collector << 'CAST('
308
+ collector << "CAST("
408
309
  collector = visit o.left, collector
409
- collector << ' AS '
410
- collector = visit Arel::Nodes::SqlLiteral.new(as_attr), collector
411
- collector << ')'
310
+ collector << " AS "
311
+ case o.as_attr
312
+ when :string
313
+ as_attr = Arel::Nodes::SqlLiteral.new('char')
314
+ when :time
315
+ as_attr = Arel::Nodes::SqlLiteral.new('time')
316
+ when :int
317
+ as_attr = Arel::Nodes::SqlLiteral.new('signed')
318
+ when :number, :decimal
319
+ as_attr = Arel::Nodes::SqlLiteral.new('decimal(20,6)')
320
+ when :datetime
321
+ as_attr = Arel::Nodes::SqlLiteral.new('datetime')
322
+ when :date
323
+ as_attr = Arel::Nodes::SqlLiteral.new('date')
324
+ when :binary
325
+ as_attr = Arel::Nodes::SqlLiteral.new('binary')
326
+ else
327
+ as_attr = Arel::Nodes::SqlLiteral.new(o.as_attr.to_s)
328
+ end
329
+ collector = visit as_attr, collector
330
+ collector << ")"
412
331
  collector
413
332
  end
414
333
 
415
- alias_method(:old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement) rescue nil
334
+ alias_method :old_visit_Arel_Nodes_SelectStatement, :visit_Arel_Nodes_SelectStatement
416
335
  def visit_Arel_Nodes_SelectStatement o, collector
417
336
  if !(collector.value.blank? || (collector.value.is_a?(Array) && collector.value[0].blank?)) && o.limit.blank? && o.offset.blank?
418
337
  o = o.dup
419
338
  o.orders = []
420
339
  end
421
- old_visit_Arel_Nodes_SelectStatement(o, collector)
340
+ old_visit_Arel_Nodes_SelectStatement(o,collector)
422
341
  end
423
342
 
424
- alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
343
+ alias_method :old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As
425
344
  def visit_Arel_Nodes_As o, collector
426
345
  if o.left.is_a?(Arel::Nodes::Binary)
427
346
  collector << '('
@@ -430,56 +349,44 @@ module ArelExtensions
430
349
  else
431
350
  collector = visit o.left, collector
432
351
  end
433
- collector << ' AS '
434
-
435
- # sometimes these values are already quoted, if they are, don't double quote it
436
- quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '`' && o.right[-1] != '`'
437
-
438
- collector << '`' if quote
352
+ collector << " AS `"
439
353
  collector = visit o.right, collector
440
- collector << '`' if quote
441
-
354
+ collector << "`"
442
355
  collector
443
356
  end
444
357
 
445
358
  def visit_ArelExtensions_Nodes_FormattedNumber o, collector
446
359
  col = o.left.coalesce(0)
447
- params = o.locale ? [o.precision, Arel.quoted(o.locale)] : [o.precision]
448
- sign = Arel.when(col < 0).
360
+ params = o.locale ? [o.precision,Arel::Nodes.build_quoted(o.locale)] : [o.precision]
361
+ sign = ArelExtensions::Nodes::Case.new.when(col<0).
449
362
  then('-').
450
363
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
451
364
  sign_length = ArelExtensions::Nodes::Length.new([sign])
452
365
 
453
- number =
454
- if o.scientific_notation
455
- ArelExtensions::Nodes::Concat.new([
456
- Arel::Nodes::NamedFunction.new('FORMAT', [
457
- col.abs / Arel.quoted(10).pow(col.abs.log10.floor)
458
- ] + params),
459
- o.type,
460
- Arel::Nodes::NamedFunction.new('FORMAT', [
461
- col.abs.log10.floor,
462
- 0
463
- ])
366
+ if o.scientific_notation
367
+ number = ArelExtensions::Nodes::Concat.new([
368
+ Arel::Nodes::NamedFunction.new('FORMAT',[
369
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor)
370
+ ]+params),
371
+ o.type,
372
+ Arel::Nodes::NamedFunction.new('FORMAT',[
373
+ col.abs.log10.floor,
374
+ 0
464
375
  ])
465
- else
466
- Arel::Nodes::NamedFunction.new('FORMAT', [col.abs] + params)
467
- end
376
+ ])
377
+ else
378
+ number = Arel::Nodes::NamedFunction.new('FORMAT',[col.abs]+params)
379
+ end
468
380
 
469
- repeated_char =
470
- if o.width == 0
471
- Arel.quoted('')
472
- else
473
- Arel
474
- .when(Arel.quoted(o.width).abs - (number.length + sign_length) > 0)
475
- .then(Arel.quoted(
476
- o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
477
- ).repeat(Arel.quoted(o.width).abs - (number.length + sign_length))
478
- )
479
- .else('')
480
- end
481
- before = !o.flags.include?('0') && !o.flags.include?('-') ? repeated_char : ''
482
- middle = o.flags.include?('0') && !o.flags.include?('-') ? repeated_char : ''
381
+ repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
382
+ when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
383
+ then(Arel::Nodes.build_quoted(
384
+ o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
385
+ ).repeat(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length))
386
+ ).
387
+ else('')
388
+ before = (!o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
389
+ middle = (o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
483
390
  after = o.flags.include?('-') ? repeated_char : ''
484
391
  full_number = ArelExtensions::Nodes::Concat.new([
485
392
  before,
@@ -488,97 +395,52 @@ module ArelExtensions
488
395
  number,
489
396
  after
490
397
  ])
491
- collector = visit ArelExtensions::Nodes::Concat.new([Arel.quoted(o.prefix), full_number, Arel.quoted(o.suffix)]), collector
492
- collector
493
- end
494
-
495
- def visit_Aggregate_For_AggregateFunction o, collector
496
- if !window_supported?
497
- warn('Warning: ArelExtensions: Window Functions are not available in the current version of the DBMS.')
498
- return collector
499
- end
500
-
501
- if !o.order.empty? || !o.group.empty?
502
- collector << ' OVER ('
503
- if !o.group.empty?
504
- collector << ' PARTITION BY ('
505
- visit o.group, collector
506
- collector << ')'
507
- end
508
- if !o.order.empty?
509
- collector << ' ORDER BY ('
510
- visit o.order, collector
511
- collector << ')'
512
- end
513
- collector << ')'
514
- end
398
+ collector = visit ArelExtensions::Nodes::Concat.new([Arel::Nodes.build_quoted(o.prefix),full_number,Arel::Nodes.build_quoted(o.suffix)]), collector
515
399
  collector
516
400
  end
517
401
 
518
402
  def visit_ArelExtensions_Nodes_Std o, collector
519
- collector << (o.unbiased_estimator ? 'STDDEV_SAMP(' : 'STDDEV_POP(')
403
+ collector << (o.unbiased_estimator ? "STDDEV_SAMP(" : "STDDEV_POP(")
520
404
  visit o.left, collector
521
- collector << ')'
522
- visit_Aggregate_For_AggregateFunction o, collector
405
+ collector << ")"
523
406
  collector
524
407
  end
525
408
 
526
409
  def visit_ArelExtensions_Nodes_Variance o, collector
527
- collector << (o.unbiased_estimator ? 'VAR_SAMP(' : 'VAR_POP(')
410
+ collector << (o.unbiased_estimator ? "VAR_SAMP(" : "VAR_POP(")
528
411
  visit o.left, collector
529
- collector << ')'
530
- visit_Aggregate_For_AggregateFunction o, collector
412
+ collector << ")"
531
413
  collector
532
414
  end
533
415
 
534
- # JSON if implemented only after 10.2.3 (aggregations after 10.5.0) in MariaDb and 5.7 (aggregations after 5.7.22) in MySql
416
+ # JSON if implemented only after 10.2.3 in MariaDb and 5.7 in MySql
535
417
  def json_supported?
536
- version_supported?('10.5.0', '5.7.22')
537
- end
538
-
539
- def window_supported?
540
- version_supported?('10.2.3', '8.0')
541
- end
542
-
543
- def regexp_replace_supported?
544
- version_supported?('10.0.5', '8.0')
545
- end
546
-
547
- def version_supported?(mariadb_v = '10.2.3', mysql_v = '5.7.0')
548
- conn = Arel::Table.engine.connection
549
- conn.send(:mariadb?) && \
550
- (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mariadb_v || \
551
- conn.respond_to?(:version) && conn.send(:version) >= mariadb_v || \
552
- conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mariadb_v) || \
553
- !conn.send(:mariadb?) && \
554
- (conn.respond_to?(:get_database_version) && conn.send(:get_database_version) >= mysql_v || \
555
- conn.respond_to?(:version) && conn.send(:version) >= mysql_v || \
556
- conn.instance_variable_get(:"@version") && conn.instance_variable_get(:"@version") >= mysql_v)
557
- # ideally we should parse the instance_variable @full_version because @version contains only the supposedly
558
- # corresponding mysql version of the current mariadb version (which is not very helpful most of the time)
418
+ Arel::Table.engine.connection.send(:mariadb?) &&
419
+ Arel::Table.engine.connection.send(:version) >= '10.2.3' ||
420
+ !Arel::Table.engine.connection.send(:mariadb?) &&
421
+ Arel::Table.engine.connection.send(:version) >= '5.7.0'
559
422
  end
560
423
 
561
- def visit_ArelExtensions_Nodes_Json o, collector
424
+ def visit_ArelExtensions_Nodes_Json o,collector
562
425
  return super if !json_supported?
563
-
564
426
  case o.dict
565
427
  when Array
566
428
  collector << 'JSON_ARRAY('
567
- o.dict.each.with_index do |v, i|
429
+ o.dict.each.with_index do |v,i|
568
430
  if i != 0
569
- collector << COMMA
431
+ collector << Arel::Visitors::MySQL::COMMA
570
432
  end
571
433
  collector = visit v, collector
572
434
  end
573
435
  collector << ')'
574
436
  when Hash
575
437
  collector << 'JSON_OBJECT('
576
- o.dict.each.with_index do |(k, v), i|
438
+ o.dict.each.with_index do |(k,v),i|
577
439
  if i != 0
578
- collector << COMMA
440
+ collector << Arel::Visitors::MySQL::COMMA
579
441
  end
580
442
  collector = visit k, collector
581
- collector << COMMA
443
+ collector << Arel::Visitors::MySQL::COMMA
582
444
  collector = visit v, collector
583
445
  end
584
446
  collector << ')'
@@ -588,11 +450,11 @@ module ArelExtensions
588
450
  collector
589
451
  end
590
452
 
591
- def visit_ArelExtensions_Nodes_JsonMerge o, collector
453
+ def visit_ArelExtensions_Nodes_JsonMerge o,collector
592
454
  collector << 'JSON_MERGE_PATCH('
593
- o.expressions.each.with_index do |v, i|
455
+ o.expressions.each.with_index do |v,i|
594
456
  if i != 0
595
- collector << COMMA
457
+ collector << Arel::Visitors::MySQL::COMMA
596
458
  end
597
459
  collector = visit v, collector
598
460
  end
@@ -600,29 +462,29 @@ module ArelExtensions
600
462
  collector
601
463
  end
602
464
 
603
- def visit_ArelExtensions_Nodes_JsonGet o, collector
465
+ def visit_ArelExtensions_Nodes_JsonGet o,collector
604
466
  collector << 'JSON_EXTRACT('
605
467
  collector = visit o.dict, collector
606
- collector << COMMA
468
+ collector << Arel::Visitors::MySQL::COMMA
607
469
  if o.key.is_a?(Integer)
608
470
  collector << "\"$[#{o.key}]\""
609
471
  else
610
- collector = visit Arel.quoted('$.') + o.key, collector
472
+ collector = visit Arel::Nodes.build_quoted('$.')+o.key, collector
611
473
  end
612
474
  collector << ')'
613
475
  collector
614
476
  end
615
477
 
616
- def visit_ArelExtensions_Nodes_JsonSet o, collector
478
+ def visit_ArelExtensions_Nodes_JsonSet o,collector
617
479
  collector << 'JSON_SET('
618
480
  collector = visit o.dict, collector
619
- collector << COMMA
481
+ collector << Arel::Visitors::MySQL::COMMA
620
482
  if o.key.is_a?(Integer)
621
483
  collector << "\"$[#{o.key}]\""
622
484
  else
623
- collector = visit Arel.quoted('$.') + o.key, collector
485
+ collector = visit Arel::Nodes.build_quoted('$.')+o.key, collector
624
486
  end
625
- collector << COMMA
487
+ collector << Arel::Visitors::MySQL::COMMA
626
488
  collector = visit o.value, collector
627
489
  collector << ')'
628
490
  collector
@@ -630,7 +492,6 @@ module ArelExtensions
630
492
 
631
493
  def visit_ArelExtensions_Nodes_JsonGroup o, collector
632
494
  return super if !json_supported?
633
-
634
495
  if o.as_array
635
496
  collector << 'JSON_ARRAYAGG('
636
497
  collector = visit o.dict, collector
@@ -639,13 +500,13 @@ module ArelExtensions
639
500
  case o.dict
640
501
  when Hash
641
502
  collector << 'JSON_MERGE_PATCH(' if o.dict.length > 1
642
- o.dict.each.with_index do |(k, v), i|
503
+ o.dict.each.with_index do |(k,v),i|
643
504
  if i != 0
644
- collector << COMMA
505
+ collector << Arel::Visitors::MySQL::COMMA
645
506
  end
646
507
  collector << 'JSON_OBJECTAGG('
647
508
  collector = visit k, collector
648
- collector << COMMA
509
+ collector << Arel::Visitors::MySQL::COMMA
649
510
  collector = visit v, collector
650
511
  collector << ')'
651
512
  end
@@ -658,6 +519,7 @@ module ArelExtensions
658
519
  end
659
520
  collector
660
521
  end
522
+
661
523
  end
662
524
  end
663
525
  end