arel_extensions 1.5.2 → 2.0.0.rc3

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