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,259 +1,126 @@
1
1
  module ArelExtensions
2
2
  module Visitors
3
3
  module MSSQL
4
-
5
- MSSQL_CLASS_NAMES = %i[MSSQL SQLServer].freeze
6
-
7
- mssql_class =
8
- Arel::Visitors
9
- .constants
10
- .select { |c| Arel::Visitors.const_get(c).is_a?(Class) }
11
- .find { |c| MSSQL_CLASS_NAMES.include?(c) }
12
-
13
- # This guard is necessary because:
14
- #
15
- # 1. const_get(mssql_class) will fail when mssql_class is nil.
16
- # 2. mssql_class could be nil under certain conditions:
17
- # 1. especially on ruby 2.5 (and surprisingly not jruby 9.2) and 3.0+.
18
- # 2. when not working with mssql itself.
19
- if mssql_class
20
- LOADED_VISITOR = Arel::Visitors.const_get(mssql_class)
21
-
22
- LOADED_VISITOR::DATE_MAPPING = {
23
- 'd' => 'day', 'm' => 'month', 'y' => 'year',
24
- 'wd' => 'weekday', 'w' => 'week',
25
- 'h' => 'hour', 'mn' => 'minute', 's' => 'second'
26
- }.freeze
27
-
28
- LOADED_VISITOR::DATE_FORMAT_DIRECTIVES = {
29
- '%Y' => 'YYYY', '%C' => '', '%y' => 'YY', '%m' => 'MM', '%B' => 'month', '%^B' => '', '%b' => '', '%^b' => '', # year, month
30
- '%V' => 'iso_week', '%G' => '', # ISO week number and year of week
31
- '%d' => 'DD', '%e' => '' , '%j' => '' , '%w' => 'dw', %'a' => '', '%A' => 'weekday', # day, weekday
32
- '%H' => 'hh', '%k' => '' , '%I' => '' , '%l' => '' , '%P' => '', '%p' => '', # hours
33
- '%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
34
- }.freeze
35
-
36
- LOADED_VISITOR::DATE_FORMAT_FORMAT = {
37
- 'YY' => '0#', 'MM' => '0#', 'DD' => '0#',
38
- 'hh' => '0#', 'mi' => '0#', 'ss' => '0#',
39
- 'iso_week' => '0#'
40
- }
41
-
42
- LOADED_VISITOR::DATE_NAME = [
43
- '%B', '%A'
44
- ]
45
-
46
- LOADED_VISITOR::DATE_FORMAT_REGEX =
47
- Regexp.new(
48
- LOADED_VISITOR::DATE_FORMAT_DIRECTIVES
49
- .keys
50
- .map{|k| Regexp.escape(k)}
51
- .join('|')
52
- ).freeze
53
-
54
- # TODO; all others... http://www.sql-server-helper.com/tips/date-formats.aspx
55
- LOADED_VISITOR::DATE_CONVERT_FORMATS = {
56
- 'YYYY-MM-DD' => 120,
57
- 'YY-MM-DD' => 120,
58
- 'MM/DD/YYYY' => 101,
59
- 'MM-DD-YYYY' => 110,
60
- 'YYYY/MM/DD' => 111,
61
- 'DD-MM-YYYY' => 105,
62
- 'DD-MM-YY' => 5,
63
- 'DD.MM.YYYY' => 104,
64
- 'YYYY-MM-DDTHH:MM:SS:MMM' => 126
65
- }.freeze
66
- end
67
-
68
- # Quoting in JRuby + AR < 5 requires special handling for MSSQL.
69
- #
70
- # It relied on @connection.quote, which in turn relied on column type for
71
- # quoting. We need only to rely on the value type.
72
- #
73
- # It didn't handle numbers correctly: `quote(1, nil)` translated into
74
- # `N'1'` which we don't want.
75
- #
76
- # The following is adapted from
77
- # https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
78
- #
79
- if RUBY_PLATFORM == 'java' && Arel::VERSION.to_i <= 6
80
- def quote_string(s)
81
- s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
82
- end
83
-
84
- def quoted_binary(value) # :nodoc:
85
- "'#{quote_string(value.to_s)}'"
86
- end
87
-
88
- def quoted_date(value)
89
- if value.acts_like?(:time)
90
- if (ActiveRecord.respond_to?(:default_timezone) && ActiveRecord.default_timezone == :utc) || ActiveRecord::Base.default_timezone == :utc
91
- value = value.getutc if value.respond_to?(:getutc) && !value.utc?
92
- else
93
- value = value.getlocal if value.respond_to?(:getlocal)
94
- end
95
- end
96
- # new versions of AR use `to_fs`, but we want max compatibility, and we're
97
- # not going to write it over and over, so it's fine like that.
98
- result = value.to_formatted_s(:db)
99
- if value.respond_to?(:usec) && value.usec > 0
100
- result << '.' << sprintf('%06d', value.usec)
101
- else
102
- result
103
- end
104
- end
105
-
106
- def quoted_true
107
- 'TRUE'
108
- end
109
-
110
- def quoted_false
111
- 'FALSE'
112
- end
113
-
114
- def quoted_time(value) # :nodoc:
115
- value = value.change(year: 2000, month: 1, day: 1)
116
- quoted_date(value).sub(/\A\d{4}-\d{2}-\d{2} /, "")
117
- end
118
-
119
- def quote value, column = nil
120
- case value
121
- when Arel::Nodes::SqlLiteral
122
- value
123
- when String, Symbol, ActiveSupport::Multibyte::Chars
124
- "'#{quote_string(value.to_s)}'"
125
- when true
126
- quoted_true
127
- when false
128
- quoted_false
129
- when nil
130
- 'NULL'
131
- # BigDecimals need to be put in a non-normalized form and quoted.
132
- when BigDecimal
133
- value.to_s('F')
134
- when Numeric, ActiveSupport::Duration
135
- value.to_s
136
- when Arel::VERSION.to_i > 6 && ActiveRecord::Type::Time::Value
137
- "'#{quoted_time(value)}'"
138
- when Date, Time
139
- "'#{quoted_date(value)}'"
140
- when Class
141
- "'#{value}'"
142
- else
143
- raise TypeError, "can't quote #{value.class.name}"
144
- end
145
- end
146
- end
147
-
148
- alias_method(:old_primary_Key_From_Table, :primary_Key_From_Table) rescue nil
149
- def primary_Key_From_Table t
150
- return unless t
151
-
152
- column_name = @connection.schema_cache.primary_keys(t.name) ||
153
- @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
154
- column_name ? t[column_name] : nil
155
- end
4
+ Arel::Visitors::MSSQL::DATE_MAPPING = {'d' => 'day', 'm' => 'month', 'y' => 'year', 'wd' => 'weekday', 'w' => 'week', 'h' => 'hour', 'mn' => 'minute', 's' => 'second'}
5
+ Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES = {
6
+ '%Y' => 'YYYY', '%C' => '', '%y' => 'YY', '%m' => 'MM', '%B' => '', '%b' => '', '%^b' => '', # year, month
7
+ '%d' => 'DD', '%e' => '', '%j' => '', '%w' => 'dw', '%A' => '', # day, weekday
8
+ '%H' => 'hh', '%k' => '', '%I' => '', '%l' => '', '%P' => '', '%p' => '', # hours
9
+ '%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
10
+ }
11
+ # TODO; all others... http://www.sql-server-helper.com/tips/date-formats.aspx
12
+ Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS = {
13
+ 'YYYY-MM-DD' => 120,
14
+ 'YY-MM-DD' => 120,
15
+ 'MM/DD/YYYY' => 101,
16
+ 'MM-DD-YYYY' => 110,
17
+ 'YYYY/MM/DD' => 111,
18
+ 'DD-MM-YYYY' => 105,
19
+ 'DD-MM-YY' => 5,
20
+ 'DD.MM.YYYY' => 104,
21
+ 'YYYY-MM-DDTHH:MM:SS:MMM' => 126
22
+ }
156
23
 
157
24
  # Math Functions
158
25
  def visit_ArelExtensions_Nodes_Ceil o, collector
159
- collector << 'CEILING('
26
+ collector << "CEILING("
160
27
  collector = visit o.expr, collector
161
- collector << ')'
28
+ collector << ")"
162
29
  collector
163
30
  end
164
31
 
165
32
  def visit_ArelExtensions_Nodes_Log10 o, collector
166
- collector << 'LOG10('
33
+ collector << "LOG10("
167
34
  o.expressions.each_with_index { |arg, i|
168
- collector << Arel::Visitors::ToSql::COMMA if i != 0
35
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
169
36
  collector = visit arg, collector
170
37
  }
171
- collector << ')'
38
+ collector << ")"
172
39
  collector
173
40
  end
174
41
 
175
42
  def visit_ArelExtensions_Nodes_Power o, collector
176
- collector << 'POWER('
43
+ collector << "POWER("
177
44
  o.expressions.each_with_index { |arg, i|
178
- collector << Arel::Visitors::ToSql::COMMA if i != 0
45
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
179
46
  collector = visit arg, collector
180
47
  }
181
- collector << ')'
48
+ collector << ")"
182
49
  collector
183
50
  end
184
51
 
185
52
  def visit_ArelExtensions_Nodes_IsNull o, collector
186
- collector << '('
53
+ collector << "("
187
54
  collector = visit o.expr, collector
188
- collector << ' IS NULL)'
55
+ collector << " IS NULL)"
189
56
  collector
190
57
  end
191
58
 
192
59
  def visit_ArelExtensions_Nodes_IsNotNull o, collector
193
- collector << '('
60
+ collector << "("
194
61
  collector = visit o.expr, collector
195
- collector << ' IS NOT NULL)'
62
+ collector << " IS NOT NULL)"
196
63
  collector
197
64
  end
198
65
 
199
66
  def visit_ArelExtensions_Nodes_Concat o, collector
200
- collector << 'CONCAT('
67
+ collector << "CONCAT("
201
68
  o.expressions.each_with_index { |arg, i|
202
- collector << LOADED_VISITOR::COMMA if i != 0
69
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
203
70
  collector = visit arg, collector
204
71
  }
205
- collector << ')'
72
+ collector << ")"
206
73
  collector
207
74
  end
208
75
 
209
76
  def visit_ArelExtensions_Nodes_Repeat o, collector
210
- collector << 'REPLICATE('
77
+ collector << "REPLICATE("
211
78
  o.expressions.each_with_index { |arg, i|
212
- collector << Arel::Visitors::ToSql::COMMA if i != 0
79
+ collector << Arel::Visitors::ToSql::COMMA unless i == 0
213
80
  collector = visit arg, collector
214
81
  }
215
- collector << ')'
82
+ collector << ")"
216
83
  collector
217
84
  end
218
85
 
219
86
 
220
87
 
221
88
  def visit_ArelExtensions_Nodes_DateDiff o, collector
222
- case o.right_node_type
223
- when :ruby_date, :ruby_time, :date, :datetime, :time
224
- collector << case o.left_node_type
225
- when :ruby_time, :datetime, :time then 'DATEDIFF(second'
226
- else 'DATEDIFF(day'
89
+ 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
90
+ collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
91
+ 'DATEDIFF(second'
92
+ else
93
+ 'DATEDIFF(day'
227
94
  end
228
- collector << LOADED_VISITOR::COMMA
95
+ collector << Arel::Visitors::MSSQL::COMMA
229
96
  collector = visit o.right, collector
230
- collector << LOADED_VISITOR::COMMA
97
+ collector << Arel::Visitors::MSSQL::COMMA
231
98
  collector = visit o.left, collector
232
99
  collector << ')'
233
100
  else
234
101
  da = ArelExtensions::Nodes::DateAdd.new([])
235
- collector << 'DATEADD('
102
+ collector << "DATEADD("
236
103
  collector = visit da.mssql_datepart(o.right), collector
237
- collector << LOADED_VISITOR::COMMA
238
- collector << '-('
104
+ collector << Arel::Visitors::MSSQL::COMMA
105
+ collector << "-("
239
106
  collector = visit da.mssql_value(o.right), collector
240
- collector << ')'
241
- collector << LOADED_VISITOR::COMMA
107
+ collector << ")"
108
+ collector << Arel::Visitors::MSSQL::COMMA
242
109
  collector = visit o.left, collector
243
- collector << ')'
110
+ collector << ")"
244
111
  collector
245
112
  end
246
113
  collector
247
114
  end
248
115
 
249
116
  def visit_ArelExtensions_Nodes_DateAdd o, collector
250
- collector << 'DATEADD('
117
+ collector << "DATEADD("
251
118
  collector = visit o.mssql_datepart(o.right), collector
252
- collector << LOADED_VISITOR::COMMA
119
+ collector << Arel::Visitors::MSSQL::COMMA
253
120
  collector = visit o.mssql_value(o.right), collector
254
- collector << LOADED_VISITOR::COMMA
121
+ collector << Arel::Visitors::MSSQL::COMMA
255
122
  collector = visit o.left, collector
256
- collector << ')'
123
+ collector << ")"
257
124
  collector
258
125
  end
259
126
 
@@ -262,77 +129,68 @@ module ArelExtensions
262
129
  collector = visit o.right, collector
263
130
  else
264
131
  left = o.left.end_with?('i') ? o.left[0..-2] : o.left
265
- conv = %w[h mn s].include?(o.left)
132
+ conv = ['h', 'mn', 's'].include?(o.left)
266
133
  collector << 'DATEPART('
267
- collector << LOADED_VISITOR::DATE_MAPPING[left]
268
- collector << LOADED_VISITOR::COMMA
134
+ collector << Arel::Visitors::MSSQL::DATE_MAPPING[left]
135
+ collector << Arel::Visitors::MSSQL::COMMA
269
136
  collector << 'CONVERT(datetime,' if conv
270
137
  collector = visit o.right, collector
271
138
  collector << ')' if conv
272
- collector << ')'
139
+ collector << ")"
273
140
  end
274
141
  collector
275
142
  end
276
143
 
277
144
  def visit_ArelExtensions_Nodes_Length o, collector
278
- if o.bytewise
279
- collector << '(DATALENGTH('
280
- collector = visit o.expr, collector
281
- collector << ') / ISNULL(NULLIF(DATALENGTH(LEFT(COALESCE('
282
- collector = visit o.expr, collector
283
- collector << ", '#' ), 1 )), 0), 1))"
284
- collector
285
- else
286
- collector << 'LEN('
287
- collector = visit o.expr, collector
288
- collector << ')'
289
- collector
290
- end
145
+ collector << "LEN("
146
+ collector = visit o.expr, collector
147
+ collector << ")"
148
+ collector
291
149
  end
292
150
 
293
151
  def visit_ArelExtensions_Nodes_Round o, collector
294
- collector << 'ROUND('
152
+ collector << "ROUND("
295
153
  o.expressions.each_with_index { |arg, i|
296
- collector << LOADED_VISITOR::COMMA if i != 0
154
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
297
155
  collector = visit arg, collector
298
156
  }
299
157
  if o.expressions.length == 1
300
- collector << LOADED_VISITOR::COMMA
301
- collector << '0'
158
+ collector << Arel::Visitors::MSSQL::COMMA
159
+ collector << "0"
302
160
  end
303
- collector << ')'
161
+ collector << ")"
304
162
  collector
305
163
  end
306
164
 
307
165
  def visit_ArelExtensions_Nodes_Locate o, collector
308
- collector << 'CHARINDEX('
166
+ collector << "CHARINDEX("
309
167
  collector = visit o.right, collector
310
- collector << LOADED_VISITOR::COMMA
168
+ collector << Arel::Visitors::MSSQL::COMMA
311
169
  collector = visit o.left, collector
312
- collector << ')'
170
+ collector << ")"
313
171
  collector
314
172
  end
315
173
 
316
174
  def visit_ArelExtensions_Nodes_Substring o, collector
317
175
  collector << 'SUBSTRING('
318
176
  collector = visit o.expressions[0], collector
319
- collector << LOADED_VISITOR::COMMA
177
+ collector << Arel::Visitors::MSSQL::COMMA
320
178
  collector = visit o.expressions[1], collector
321
- collector << LOADED_VISITOR::COMMA
179
+ collector << Arel::Visitors::MSSQL::COMMA
322
180
  collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
323
181
  collector << ')'
324
182
  collector
325
183
  end
326
184
 
327
185
  def visit_ArelExtensions_Nodes_Trim o, collector
328
- # NOTE: in MSSQL's `blank`, o.right is the space char so we need to
329
- # account for it.
330
- if o.right && !/\A\s\Z/.match(o.right.expr)
331
- collector << 'dbo.TrimChar('
186
+ if o.right
187
+ collector << "REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(REPLACE("
332
188
  collector = visit o.left, collector
333
- collector << Arel::Visitors::MSSQL::COMMA
189
+ collector << ", ' ', '~'), "
334
190
  collector = visit o.right, collector
335
- collector << ')'
191
+ collector << ", ' '))), ' ', "
192
+ collector = visit o.right, collector
193
+ collector << "), '~', ' ')"
336
194
  else
337
195
  collector << "LTRIM(RTRIM("
338
196
  collector = visit o.left, collector
@@ -343,7 +201,7 @@ module ArelExtensions
343
201
 
344
202
  def visit_ArelExtensions_Nodes_Ltrim o, collector
345
203
  if o.right
346
- collector << 'REPLACE(REPLACE(LTRIM(REPLACE(REPLACE('
204
+ collector << "REPLACE(REPLACE(LTRIM(REPLACE(REPLACE("
347
205
  collector = visit o.left, collector
348
206
  collector << ", ' ', '~'), "
349
207
  collector = visit o.right, collector
@@ -351,16 +209,16 @@ module ArelExtensions
351
209
  collector = visit o.right, collector
352
210
  collector << "), '~', ' ')"
353
211
  else
354
- collector << 'LTRIM('
212
+ collector << "LTRIM("
355
213
  collector = visit o.left, collector
356
- collector << ')'
214
+ collector << ")"
357
215
  end
358
216
  collector
359
217
  end
360
218
 
361
219
  def visit_ArelExtensions_Nodes_Rtrim o, collector
362
220
  if o.right
363
- collector << 'REPLACE(REPLACE(RTRIM(REPLACE(REPLACE('
221
+ collector << "REPLACE(REPLACE(RTRIM(REPLACE(REPLACE("
364
222
  collector = visit o.left, collector
365
223
  collector << ", ' ', '~'), "
366
224
  collector = visit o.right, collector
@@ -368,9 +226,9 @@ module ArelExtensions
368
226
  collector = visit o.right, collector
369
227
  collector << "), '~', ' ')"
370
228
  else
371
- collector << 'RTRIM('
229
+ collector << "RTRIM("
372
230
  collector = visit o.left, collector
373
- collector << ')'
231
+ collector << ")"
374
232
  end
375
233
  collector
376
234
  end
@@ -384,102 +242,64 @@ module ArelExtensions
384
242
  end
385
243
 
386
244
  def visit_ArelExtensions_Nodes_Format o, collector
387
- visit_ArelExtensions_Nodes_FormattedDate o, collector
388
- end
389
-
390
- def visit_ArelExtensions_Nodes_FormattedDate o, collector
391
- f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, LOADED_VISITOR::DATE_FORMAT_DIRECTIVES)
392
- if fmt = LOADED_VISITOR::DATE_CONVERT_FORMATS[f]
245
+ f = o.iso_format.dup
246
+ Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES.each { |d, r| f.gsub!(d, r) }
247
+ if Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f]
393
248
  collector << "CONVERT(VARCHAR(#{f.length})"
394
- collector << LOADED_VISITOR::COMMA
395
- if o.time_zone
396
- collector << 'CONVERT(datetime'
397
- collector << LOADED_VISITOR::COMMA
398
- collector << ' '
399
- end
249
+ collector << Arel::Visitors::MSSQL::COMMA
400
250
  collector = visit o.left, collector
401
- case o.time_zone
402
- when Hash
403
- src_tz, dst_tz = o.time_zone.first
404
- collector << ') AT TIME ZONE '
405
- collector = visit Arel.quoted(src_tz), collector
406
- collector << ' AT TIME ZONE '
407
- collector = visit Arel.quoted(dst_tz), collector
408
- when String
409
- collector << ') AT TIME ZONE '
410
- collector = visit Arel.quoted(o.time_zone), collector
411
- end
412
- collector << LOADED_VISITOR::COMMA
413
- collector << fmt.to_s
251
+ collector << Arel::Visitors::MSSQL::COMMA
252
+ collector << Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f].to_s
414
253
  collector << ')'
415
254
  collector
416
255
  else
417
- s = StringScanner.new o.iso_format
418
- collector << '('
419
- sep = ''
420
- while !s.eos?
421
- collector << sep
422
- sep = ' + '
423
- case
424
- when s.scan(LOADED_VISITOR::DATE_FORMAT_REGEX)
425
- dir = LOADED_VISITOR::DATE_FORMAT_DIRECTIVES[s.matched]
426
- fmt = LOADED_VISITOR::DATE_FORMAT_FORMAT[dir]
427
- date_name = LOADED_VISITOR::DATE_NAME.include?(s.matched)
428
- collector << 'LTRIM(RTRIM('
429
- collector << 'FORMAT(' if fmt
430
- collector << 'STR(' if !fmt && !date_name
431
- collector << (date_name ? 'DATENAME(' : 'DATEPART(')
432
- collector << dir
433
- collector << LOADED_VISITOR::COMMA
434
- if o.time_zone
435
- collector << 'CONVERT(datetime'
436
- collector << LOADED_VISITOR::COMMA
437
- collector << ' '
256
+ collector << "("
257
+ t = o.iso_format.split('%')
258
+ t.each_with_index {|str, i|
259
+ if i == 0 && t[0] != '%'
260
+ collector = visit Arel::Nodes.build_quoted(str), collector
261
+ if str.length > 1
262
+ collector << Arel::Visitors::MSSQL::COMMA
263
+ collector = visit Arel::Nodes.build_quoted(str.sub(/\A./, '')), collector
438
264
  end
439
- collector = visit o.left, collector
440
- case o.time_zone
441
- when Hash
442
- src_tz, dst_tz = o.time_zone.first.first, o.time_zone.first.second
443
- collector << ') AT TIME ZONE '
444
- collector = visit Arel.quoted(src_tz), collector
445
- collector << ' AT TIME ZONE '
446
- collector = visit Arel.quoted(dst_tz), collector
447
- when String
448
- collector << ') AT TIME ZONE '
449
- collector = visit Arel.quoted(o.time_zone), collector
265
+ elsif str.length > 0
266
+ if !Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES['%' + str[0]].blank?
267
+ collector << 'LTRIM(STR(DATEPART('
268
+ collector << Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES['%' + str[0]]
269
+ collector << Arel::Visitors::MSSQL::COMMA
270
+ collector = visit o.left, collector
271
+ collector << ')))'
272
+ if str.length > 1
273
+ collector << ' + '
274
+ collector = visit Arel::Nodes.build_quoted(str.sub(/\A./, '')), collector
275
+ end
450
276
  end
451
- collector << ')'
452
- collector << ')' if !fmt && !date_name
453
- collector << LOADED_VISITOR::COMMA << "'#{fmt}')" if fmt
454
- collector << '))'
455
- when s.scan(/^%%/)
456
- collector = visit Arel.quoted('%'), collector
457
- when s.scan(/[^%]+|./)
458
- collector = visit Arel.quoted(s.matched), collector
459
277
  end
460
- end
278
+ collector << ' + ' if t[i + 1]
279
+ }
280
+
461
281
  collector << ')'
462
282
  collector
463
283
  end
464
284
  end
465
285
 
466
286
  def visit_ArelExtensions_Nodes_Replace o, collector
467
- collector << 'REPLACE('
287
+ collector << "REPLACE("
468
288
  o.expressions.each_with_index { |arg, i|
469
- collector << LOADED_VISITOR::COMMA if i != 0
289
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
470
290
  collector = visit arg, collector
471
291
  }
472
- collector << ')'
292
+ collector << ")"
473
293
  collector
474
294
  end
475
295
 
476
296
  def visit_ArelExtensions_Nodes_FindInSet o, collector
477
- collector << 'dbo.FIND_IN_SET('
297
+ collector << "dbo.FIND_IN_SET("
478
298
  o.expressions.each_with_index { |arg, i|
479
- collector << LOADED_VISITOR::COMMA if i != 0
299
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
480
300
  collector = visit arg, collector
481
301
  }
482
- collector << ')'
302
+ collector << ")"
483
303
  collector
484
304
  end
485
305
 
@@ -522,9 +342,9 @@ module ArelExtensions
522
342
  end
523
343
 
524
344
  def visit_ArelExtensions_Nodes_AiIMatches o, collector
525
- collector = visit o.left.collate(true, true), collector
345
+ collector = visit o.left.collate(true,true), collector
526
346
  collector << ' LIKE '
527
- collector = visit o.right.collate(true, true), collector
347
+ collector = visit o.right.collate(true,true), collector
528
348
  if o.escape
529
349
  collector << ' ESCAPE '
530
350
  visit o.escape, collector
@@ -562,34 +382,6 @@ module ArelExtensions
562
382
  collector
563
383
  end
564
384
 
565
- alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
566
- def visit_Arel_Nodes_As o, collector
567
- if o.left.is_a?(Arel::Nodes::Binary)
568
- collector << '('
569
- collector = visit o.left, collector
570
- collector << ')'
571
- else
572
- collector = visit o.left, collector
573
- end
574
- collector << ' AS '
575
- # Sometimes these values are already quoted, if they are, don't double quote it
576
- lft, rgt =
577
- if o.right.is_a?(Arel::Nodes::SqlLiteral)
578
- if Arel::VERSION.to_i >= 6 && o.right[0] != '[' && o.right[-1] != ']'
579
- # This is a lie, it's not about arel version, but SQL Server's (>= 2000).
580
- ['[', ']']
581
- elsif o.right[0] != '"' && o.right[-1] != '"'
582
- ['"', '"']
583
- else
584
- []
585
- end
586
- end
587
- collector << lft if lft
588
- collector = visit o.right, collector
589
- collector << rgt if rgt
590
- collector
591
- end
592
-
593
385
  # SQL Server does not know about REGEXP
594
386
  def visit_Arel_Nodes_Regexp o, collector
595
387
  collector = visit o.left, collector
@@ -603,121 +395,108 @@ module ArelExtensions
603
395
  collector
604
396
  end
605
397
 
606
- def visit_Arel_Nodes_RollUp(o, collector)
607
- collector << "ROLLUP"
608
- grouping_array_or_grouping_element o, collector
609
- end
610
-
398
+ # TODO;
611
399
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
612
- collector << '(STRING_AGG('
400
+ collector << "(STRING_AGG("
613
401
  collector = visit o.left, collector
614
402
  collector << Arel::Visitors::Oracle::COMMA
615
- sep = o.separator.is_a?(Arel::Nodes::Quoted) ? o.separator.expr : o.separator
616
- collector =
617
- if 'NULL' != sep
618
- visit o.separator, collector
619
- else
620
- visit Arel.quoted(','), collector
621
- end
622
- collector << ') WITHIN GROUP (ORDER BY '
623
- if o.order.present?
624
- o.order.each_with_index do |order, i|
625
- collector << Arel::Visitors::Oracle::COMMA if i != 0
403
+ if o.right && o.right != 'NULL'
404
+ collector = visit o.right, collector
405
+ else
406
+ collector = visit Arel::Nodes.build_quoted(','), collector
407
+ end
408
+ collector << ") WITHIN GROUP (ORDER BY "
409
+ if !o.orders.blank?
410
+ o.orders.each_with_index do |order,i|
411
+ collector << Arel::Visitors::Oracle::COMMA unless i == 0
626
412
  collector = visit order, collector
627
413
  end
628
414
  else
629
415
  collector = visit o.left, collector
630
416
  end
631
- collector << '))'
417
+ collector << "))"
632
418
  collector
633
419
  end
634
420
 
635
421
  def visit_ArelExtensions_Nodes_MD5 o, collector
636
422
  collector << "LOWER(CONVERT(NVARCHAR(32),HashBytes('MD5',CONVERT(VARCHAR,"
637
423
  collector = visit o.left, collector
638
- collector << ')),2))'
424
+ collector << ")),2))"
639
425
  collector
640
426
  end
641
427
 
642
428
  def visit_ArelExtensions_Nodes_Cast o, collector
643
- as_attr =
644
- case o.as_attr
645
- when :string
646
- 'varchar'
647
- when :time
648
- 'time'
649
- when :date
650
- 'date'
651
- when :datetime
652
- 'datetime'
653
- when :number, :decimal, :float
654
- 'decimal(10,6)'
655
- when :int
656
- collector << 'CAST(CAST('
657
- collector = visit o.left, collector
658
- collector << ' AS decimal(10,0)) AS int)'
659
- return collector
660
- when :binary
661
- 'binary'
662
- else
663
- o.as_attr.to_s
664
- end
665
- collector << 'CAST('
429
+ case o.as_attr
430
+ when :string
431
+ as_attr = Arel::Nodes::SqlLiteral.new('varchar')
432
+ when :time
433
+ as_attr = Arel::Nodes::SqlLiteral.new('time')
434
+ when :date
435
+ as_attr = Arel::Nodes::SqlLiteral.new('date')
436
+ when :datetime
437
+ as_attr = Arel::Nodes::SqlLiteral.new('datetime')
438
+ when :number,:decimal, :float
439
+ as_attr = Arel::Nodes::SqlLiteral.new('decimal(10,6)')
440
+ when :int
441
+ collector << "CAST(CAST("
442
+ collector = visit o.left, collector
443
+ collector << " AS decimal(10,0)) AS int)"
444
+ return collector
445
+ when :binary
446
+ as_attr = Arel::Nodes::SqlLiteral.new('binary')
447
+ else
448
+ as_attr = Arel::Nodes::SqlLiteral.new(o.as_attr.to_s)
449
+ end
450
+ collector << "CAST("
666
451
  collector = visit o.left, collector
667
- collector << ' AS '
668
- collector = visit Arel::Nodes::SqlLiteral.new(as_attr), collector
669
- collector << ')'
452
+ collector << " AS "
453
+ collector = visit as_attr, collector
454
+ collector << ")"
670
455
  collector
671
456
  end
672
457
 
673
458
  def visit_ArelExtensions_Nodes_FormattedNumber o, collector
674
459
  col = o.left.coalesce(0)
675
- locale = Arel.quoted(o.locale.tr('_', '-'))
676
- param = Arel.quoted("N#{o.precision}")
677
- sign = Arel.when(col < 0).
460
+ locale = Arel::Nodes.build_quoted(o.locale.tr('_','-'))
461
+ param = Arel::Nodes.build_quoted("N#{o.precision}")
462
+ sign = ArelExtensions::Nodes::Case.new.when(col<0).
678
463
  then('-').
679
464
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
680
465
  sign_length = o.flags.include?('+') || o.flags.include?(' ') ?
681
- Arel.quoted(1) :
682
- Arel.when(col < 0).then(1).else(0)
683
-
684
- number =
685
- if o.scientific_notation
686
- ArelExtensions::Nodes::Concat.new([
687
- Arel::Nodes::NamedFunction.new('FORMAT', [
688
- col.abs / Arel.quoted(10).pow(col.abs.log10.floor),
466
+ Arel::Nodes.build_quoted(1) :
467
+ ArelExtensions::Nodes::Case.new.when(col<0).then(1).else(0)
468
+
469
+ if o.scientific_notation
470
+ number = ArelExtensions::Nodes::Concat.new([
471
+ Arel::Nodes::NamedFunction.new('FORMAT',[
472
+ col.abs/Arel::Nodes.build_quoted(10).pow(col.abs.log10.floor),
689
473
  param,
690
474
  locale
691
475
  ]),
692
476
  o.type,
693
- Arel::Nodes::NamedFunction.new('FORMAT', [
477
+ Arel::Nodes::NamedFunction.new('FORMAT',[
694
478
  col.abs.log10.floor,
695
- Arel.quoted('N0'),
479
+ Arel::Nodes.build_quoted('N0'),
696
480
  locale
697
481
  ])
698
482
  ])
699
- else
700
- Arel::Nodes::NamedFunction.new('FORMAT', [
701
- Arel.quoted(col.abs),
483
+ else
484
+ number = Arel::Nodes::NamedFunction.new('FORMAT',[
485
+ Arel::Nodes.build_quoted(col.abs),
702
486
  param,
703
487
  locale
704
488
  ])
705
- end
489
+ end
706
490
 
707
- repeated_char =
708
- if o.width == 0
709
- Arel.quoted('')
710
- else
711
- Arel
712
- .when(Arel.quoted(o.width).abs - (number.length + sign_length) > 0)
713
- .then(Arel.quoted(
714
- o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
715
- ).repeat(Arel.quoted(o.width).abs - (number.length + sign_length))
716
- )
717
- .else('')
718
- end
719
- before = !o.flags.include?('0') && !o.flags.include?('-') ? repeated_char : ''
720
- middle = o.flags.include?('0') && !o.flags.include?('-') ? repeated_char : ''
491
+ repeated_char = (o.width == 0) ? Arel::Nodes.build_quoted('') : ArelExtensions::Nodes::Case.new().
492
+ when(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length)>0).
493
+ then(Arel::Nodes.build_quoted(
494
+ o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
495
+ ).repeat(Arel::Nodes.build_quoted(o.width).abs-(number.length+sign_length))
496
+ ).
497
+ else('')
498
+ before = (!o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
499
+ middle = (o.flags.include?('0'))&&(!o.flags.include?('-')) ? repeated_char : ''
721
500
  after = o.flags.include?('-') ? repeated_char : ''
722
501
  full_number =
723
502
  ArelExtensions::Nodes::Concat.new([
@@ -727,27 +506,27 @@ module ArelExtensions
727
506
  number,
728
507
  after
729
508
  ])
730
- collector = visit ArelExtensions::Nodes::Concat.new([Arel.quoted(o.prefix), full_number, Arel.quoted(o.suffix)]), collector
509
+ collector = visit ArelExtensions::Nodes::Concat.new([Arel::Nodes.build_quoted(o.prefix),full_number,Arel::Nodes.build_quoted(o.suffix)]), collector
731
510
  collector
732
511
  end
733
512
 
734
513
  def visit_ArelExtensions_Nodes_Std o, collector
735
- collector << (o.unbiased_estimator ? 'STDEV(' : 'STDEVP(')
514
+ collector << (o.unbiased_estimator ? "STDEV(" : "STDEVP(")
736
515
  visit o.left, collector
737
- collector << ')'
516
+ collector << ")"
738
517
  collector
739
518
  end
740
519
 
741
520
  def visit_ArelExtensions_Nodes_Variance o, collector
742
- collector << (o.unbiased_estimator ? 'VAR(' : 'VARP(')
521
+ collector << (o.unbiased_estimator ? "VAR(" : "VARP(")
743
522
  visit o.left, collector
744
- collector << ')'
523
+ collector << ")"
745
524
  collector
746
525
  end
747
526
 
748
527
 
749
528
  def visit_ArelExtensions_Nodes_LevenshteinDistance o, collector
750
- collector << 'dbo.LEVENSHTEIN_DISTANCE('
529
+ collector << "dbo.LEVENSHTEIN_DISTANCE("
751
530
  collector = visit o.left, collector
752
531
  collector << Arel::Visitors::ToSql::COMMA
753
532
  collector = visit o.right, collector
@@ -756,30 +535,20 @@ module ArelExtensions
756
535
  end
757
536
 
758
537
 
759
- def visit_ArelExtensions_Nodes_JsonGet o, collector
538
+ def visit_ArelExtensions_Nodes_JsonGet o,collector
760
539
  collector << 'JSON_VALUE('
761
540
  collector = visit o.dict, collector
762
541
  collector << Arel::Visitors::MySQL::COMMA
763
542
  if o.key.is_a?(Integer)
764
543
  collector << "\"$[#{o.key}]\""
765
544
  else
766
- collector = visit Arel.quoted('$.') + o.key, collector
545
+ collector = visit Arel::Nodes.build_quoted('$.')+o.key, collector
767
546
  end
768
547
  collector << ')'
769
548
  collector
770
549
  end
771
550
 
772
- # Utilized by GroupingSet, Cube & RollUp visitors to
773
- # handle grouping aggregation semantics
774
- def grouping_array_or_grouping_element(o, collector)
775
- if o.expr.is_a? Array
776
- collector << "( "
777
- visit o.expr, collector
778
- collector << " )"
779
- else
780
- visit o.expr, collector
781
- end
782
- end
551
+
783
552
  end
784
553
  end
785
554
  end