arel_extensions 1.4.0 → 2.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +2 -1
  3. data/.gitignore +6 -7
  4. data/.rubocop.yml +3 -67
  5. data/.travis/oracle/download.js +152 -0
  6. data/.travis/oracle/download.sh +30 -0
  7. data/.travis/oracle/download_ojdbc.js +116 -0
  8. data/.travis/oracle/install.sh +34 -0
  9. data/.travis/setup_accounts.sh +9 -0
  10. data/.travis/sqlite3/extension-functions.sh +6 -0
  11. data/.travis.yml +223 -0
  12. data/Gemfile +28 -2
  13. data/README.md +90 -239
  14. data/Rakefile +30 -48
  15. data/TODO +1 -0
  16. data/appveyor.yml +22 -60
  17. data/arel_extensions.gemspec +14 -13
  18. data/functions.html +3 -3
  19. data/gemfiles/rails3.gemfile +10 -10
  20. data/gemfiles/rails4.gemfile +29 -0
  21. data/gemfiles/rails5_0.gemfile +29 -0
  22. data/gemfiles/rails5_1_4.gemfile +14 -14
  23. data/gemfiles/rails5_2.gemfile +14 -16
  24. data/init/mssql.sql +4 -4
  25. data/init/mysql.sql +38 -38
  26. data/init/oracle.sql +0 -0
  27. data/init/postgresql.sql +21 -21
  28. data/init/sqlite.sql +0 -0
  29. data/lib/arel_extensions/attributes.rb +3 -15
  30. data/lib/arel_extensions/boolean_functions.rb +14 -53
  31. data/lib/arel_extensions/common_sql_functions.rb +17 -16
  32. data/lib/arel_extensions/comparators.rb +28 -27
  33. data/lib/arel_extensions/date_duration.rb +13 -17
  34. data/lib/arel_extensions/insert_manager.rb +15 -18
  35. data/lib/arel_extensions/math.rb +53 -55
  36. data/lib/arel_extensions/math_functions.rb +39 -46
  37. data/lib/arel_extensions/nodes/abs.rb +1 -0
  38. data/lib/arel_extensions/nodes/blank.rb +2 -1
  39. data/lib/arel_extensions/nodes/case.rb +19 -20
  40. data/lib/arel_extensions/nodes/cast.rb +8 -10
  41. data/lib/arel_extensions/nodes/ceil.rb +1 -1
  42. data/lib/arel_extensions/nodes/coalesce.rb +4 -3
  43. data/lib/arel_extensions/nodes/collate.rb +10 -9
  44. data/lib/arel_extensions/nodes/concat.rb +18 -9
  45. data/lib/arel_extensions/nodes/date_diff.rb +26 -42
  46. data/lib/arel_extensions/nodes/duration.rb +3 -0
  47. data/lib/arel_extensions/nodes/find_in_set.rb +1 -0
  48. data/lib/arel_extensions/nodes/floor.rb +1 -1
  49. data/lib/arel_extensions/nodes/format.rb +8 -35
  50. data/lib/arel_extensions/nodes/formatted_number.rb +23 -22
  51. data/lib/arel_extensions/nodes/function.rb +37 -42
  52. data/lib/arel_extensions/nodes/is_null.rb +0 -0
  53. data/lib/arel_extensions/nodes/json.rb +39 -52
  54. data/lib/arel_extensions/nodes/length.rb +0 -5
  55. data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
  56. data/lib/arel_extensions/nodes/locate.rb +2 -1
  57. data/lib/arel_extensions/nodes/log10.rb +2 -1
  58. data/lib/arel_extensions/nodes/matches.rb +7 -5
  59. data/lib/arel_extensions/nodes/md5.rb +1 -0
  60. data/lib/arel_extensions/nodes/power.rb +5 -5
  61. data/lib/arel_extensions/nodes/rand.rb +1 -0
  62. data/lib/arel_extensions/nodes/repeat.rb +5 -3
  63. data/lib/arel_extensions/nodes/replace.rb +8 -16
  64. data/lib/arel_extensions/nodes/round.rb +6 -5
  65. data/lib/arel_extensions/nodes/soundex.rb +15 -15
  66. data/lib/arel_extensions/nodes/std.rb +21 -18
  67. data/lib/arel_extensions/nodes/substring.rb +16 -8
  68. data/lib/arel_extensions/nodes/then.rb +1 -1
  69. data/lib/arel_extensions/nodes/trim.rb +6 -4
  70. data/lib/arel_extensions/nodes/union.rb +8 -5
  71. data/lib/arel_extensions/nodes/union_all.rb +7 -4
  72. data/lib/arel_extensions/nodes/wday.rb +4 -0
  73. data/lib/arel_extensions/nodes.rb +1 -1
  74. data/lib/arel_extensions/null_functions.rb +5 -19
  75. data/lib/arel_extensions/predications.rb +43 -44
  76. data/lib/arel_extensions/railtie.rb +5 -5
  77. data/lib/arel_extensions/set_functions.rb +7 -5
  78. data/lib/arel_extensions/string_functions.rb +29 -59
  79. data/lib/arel_extensions/tasks.rb +6 -6
  80. data/lib/arel_extensions/version.rb +1 -1
  81. data/lib/arel_extensions/visitors/ibm_db.rb +31 -24
  82. data/lib/arel_extensions/visitors/mssql.rb +191 -422
  83. data/lib/arel_extensions/visitors/mysql.rb +212 -350
  84. data/lib/arel_extensions/visitors/oracle.rb +178 -220
  85. data/lib/arel_extensions/visitors/oracle12.rb +31 -18
  86. data/lib/arel_extensions/visitors/postgresql.rb +173 -256
  87. data/lib/arel_extensions/visitors/sqlite.rb +126 -140
  88. data/lib/arel_extensions/visitors/to_sql.rb +237 -298
  89. data/lib/arel_extensions/visitors.rb +62 -83
  90. data/lib/arel_extensions.rb +31 -224
  91. data/test/database.yml +7 -15
  92. data/test/helper.rb +18 -0
  93. data/test/real_db_test.rb +117 -120
  94. data/test/support/fake_record.rb +3 -9
  95. data/test/test_comparators.rb +17 -14
  96. data/test/visitors/test_bulk_insert_oracle.rb +11 -11
  97. data/test/visitors/test_bulk_insert_sqlite.rb +13 -12
  98. data/test/visitors/test_bulk_insert_to_sql.rb +13 -11
  99. data/test/visitors/test_oracle.rb +55 -55
  100. data/test/visitors/test_to_sql.rb +226 -419
  101. data/test/with_ar/all_agnostic_test.rb +366 -709
  102. data/test/with_ar/insert_agnostic_test.rb +21 -27
  103. data/test/with_ar/test_bulk_sqlite.rb +16 -17
  104. data/test/with_ar/test_math_sqlite.rb +26 -26
  105. data/test/with_ar/test_string_mysql.rb +33 -31
  106. data/test/with_ar/test_string_sqlite.rb +34 -30
  107. metadata +34 -43
  108. data/.github/workflows/publish.yml +0 -29
  109. data/.github/workflows/release.yml +0 -30
  110. data/.github/workflows/ruby.yml +0 -399
  111. data/CONTRIBUTING.md +0 -102
  112. data/NEWS.md +0 -78
  113. data/bin/build +0 -15
  114. data/bin/compose +0 -6
  115. data/bin/publish +0 -8
  116. data/dev/arelx.dockerfile +0 -44
  117. data/dev/compose.yaml +0 -71
  118. data/dev/postgres.dockerfile +0 -5
  119. data/dev/rbenv +0 -189
  120. data/gemfiles/rails4_2.gemfile +0 -38
  121. data/gemfiles/rails5.gemfile +0 -29
  122. data/gemfiles/rails6.gemfile +0 -33
  123. data/gemfiles/rails6_1.gemfile +0 -33
  124. data/gemfiles/rails7.gemfile +0 -33
  125. data/gemfiles/rails7_1.gemfile +0 -33
  126. data/gemfiles/rails7_2.gemfile +0 -33
  127. data/gemspecs/arel_extensions-v1.gemspec +0 -27
  128. data/gemspecs/arel_extensions-v2.gemspec +0 -27
  129. data/generate_gems.sh +0 -15
  130. data/lib/arel_extensions/aliases.rb +0 -14
  131. data/lib/arel_extensions/helpers.rb +0 -62
  132. data/lib/arel_extensions/nodes/aggregate_function.rb +0 -13
  133. data/lib/arel_extensions/nodes/formatted_date.rb +0 -42
  134. data/lib/arel_extensions/nodes/rollup.rb +0 -36
  135. data/lib/arel_extensions/nodes/select.rb +0 -10
  136. data/lib/arel_extensions/nodes/sum.rb +0 -7
  137. data/lib/arel_extensions/visitors/convert_format.rb +0 -37
  138. data/test/arelx_test_helper.rb +0 -74
  139. data/version_v1.rb +0 -3
  140. data/version_v2.rb +0 -3
@@ -1,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
-
611
398
  # TODO;
612
399
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
613
- collector << '(STRING_AGG('
400
+ collector << "(STRING_AGG("
614
401
  collector = visit o.left, collector
615
402
  collector << Arel::Visitors::Oracle::COMMA
616
- collector =
617
- if o.separator && o.separator != 'NULL'
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