arel_extensions 1.5.3 → 2.0.0.rc3

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 (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 +25 -24
  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 -29
  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 +36 -82
  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 -721
  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 -30
  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 -109
  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 -34
  123. data/gemfiles/rails6_1.gemfile +0 -42
  124. data/gemfiles/rails7.gemfile +0 -42
  125. data/gemfiles/rails7_1.gemfile +0 -41
  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