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