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,257 +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 <= V6
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 > V6 && 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
 
86
+
87
+
219
88
  def visit_ArelExtensions_Nodes_DateDiff o, collector
220
- case o.right_node_type
221
- when :ruby_date, :ruby_time, :date, :datetime, :time
222
- collector << case o.left_node_type
223
- when :ruby_time, :datetime, :time then 'DATEDIFF(second'
224
- 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'
225
94
  end
226
- collector << LOADED_VISITOR::COMMA
95
+ collector << Arel::Visitors::MSSQL::COMMA
227
96
  collector = visit o.right, collector
228
- collector << LOADED_VISITOR::COMMA
97
+ collector << Arel::Visitors::MSSQL::COMMA
229
98
  collector = visit o.left, collector
230
99
  collector << ')'
231
100
  else
232
101
  da = ArelExtensions::Nodes::DateAdd.new([])
233
- collector << 'DATEADD('
102
+ collector << "DATEADD("
234
103
  collector = visit da.mssql_datepart(o.right), collector
235
- collector << LOADED_VISITOR::COMMA
236
- collector << '-('
104
+ collector << Arel::Visitors::MSSQL::COMMA
105
+ collector << "-("
237
106
  collector = visit da.mssql_value(o.right), collector
238
- collector << ')'
239
- collector << LOADED_VISITOR::COMMA
107
+ collector << ")"
108
+ collector << Arel::Visitors::MSSQL::COMMA
240
109
  collector = visit o.left, collector
241
- collector << ')'
110
+ collector << ")"
242
111
  collector
243
112
  end
244
113
  collector
245
114
  end
246
115
 
247
116
  def visit_ArelExtensions_Nodes_DateAdd o, collector
248
- collector << 'DATEADD('
117
+ collector << "DATEADD("
249
118
  collector = visit o.mssql_datepart(o.right), collector
250
- collector << LOADED_VISITOR::COMMA
119
+ collector << Arel::Visitors::MSSQL::COMMA
251
120
  collector = visit o.mssql_value(o.right), collector
252
- collector << LOADED_VISITOR::COMMA
121
+ collector << Arel::Visitors::MSSQL::COMMA
253
122
  collector = visit o.left, collector
254
- collector << ')'
123
+ collector << ")"
255
124
  collector
256
125
  end
257
126
 
@@ -260,94 +129,68 @@ module ArelExtensions
260
129
  collector = visit o.right, collector
261
130
  else
262
131
  left = o.left.end_with?('i') ? o.left[0..-2] : o.left
263
- conv = %w[h mn s].include?(o.left)
132
+ conv = ['h', 'mn', 's'].include?(o.left)
264
133
  collector << 'DATEPART('
265
- collector << LOADED_VISITOR::DATE_MAPPING[left]
266
- collector << LOADED_VISITOR::COMMA
134
+ collector << Arel::Visitors::MSSQL::DATE_MAPPING[left]
135
+ collector << Arel::Visitors::MSSQL::COMMA
267
136
  collector << 'CONVERT(datetime,' if conv
268
137
  collector = visit o.right, collector
269
138
  collector << ')' if conv
270
- collector << ')'
139
+ collector << ")"
271
140
  end
272
141
  collector
273
142
  end
274
143
 
275
- def visit_ArelExtensions_Nodes_ByteSize o, collector
276
- collector << 'DATALENGTH(CAST('
277
- collector = visit o.expr.coalesce(''), collector
278
- collector << ' AS VARCHAR(MAX)))'
279
- collector
280
- end
281
-
282
- def visit_ArelExtensions_Nodes_CharLength o, collector
283
- # According to the docs https://learn.microsoft.com/en-us/sql/t-sql/functions/len-transact-sql?view=sql-server-ver17
284
- # LEN doesn't take into account the trailing white space, and that's why
285
- # we need to do acrobatics.
286
- collector << 'LEN('
287
- collector = visit o.expr.coalesce(''), collector
288
- collector << " + 'x') - 1"
289
- collector
290
- end
291
-
292
144
  def visit_ArelExtensions_Nodes_Length o, collector
293
- if o.bytewise
294
- collector << '(DATALENGTH('
295
- collector = visit o.expr, collector
296
- collector << ') / ISNULL(NULLIF(DATALENGTH(LEFT(COALESCE('
297
- collector = visit o.expr, collector
298
- collector << ", '#' ), 1 )), 0), 1))"
299
- collector
300
- else
301
- collector << 'LEN('
302
- collector = visit o.expr, collector
303
- collector << ')'
304
- collector
305
- end
145
+ collector << "LEN("
146
+ collector = visit o.expr, collector
147
+ collector << ")"
148
+ collector
306
149
  end
307
150
 
308
151
  def visit_ArelExtensions_Nodes_Round o, collector
309
- collector << 'ROUND('
152
+ collector << "ROUND("
310
153
  o.expressions.each_with_index { |arg, i|
311
- collector << LOADED_VISITOR::COMMA if i != 0
154
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
312
155
  collector = visit arg, collector
313
156
  }
314
157
  if o.expressions.length == 1
315
- collector << LOADED_VISITOR::COMMA
316
- collector << '0'
158
+ collector << Arel::Visitors::MSSQL::COMMA
159
+ collector << "0"
317
160
  end
318
- collector << ')'
161
+ collector << ")"
319
162
  collector
320
163
  end
321
164
 
322
165
  def visit_ArelExtensions_Nodes_Locate o, collector
323
- collector << 'CHARINDEX('
166
+ collector << "CHARINDEX("
324
167
  collector = visit o.right, collector
325
- collector << LOADED_VISITOR::COMMA
168
+ collector << Arel::Visitors::MSSQL::COMMA
326
169
  collector = visit o.left, collector
327
- collector << ')'
170
+ collector << ")"
328
171
  collector
329
172
  end
330
173
 
331
174
  def visit_ArelExtensions_Nodes_Substring o, collector
332
175
  collector << 'SUBSTRING('
333
176
  collector = visit o.expressions[0], collector
334
- collector << LOADED_VISITOR::COMMA
177
+ collector << Arel::Visitors::MSSQL::COMMA
335
178
  collector = visit o.expressions[1], collector
336
- collector << LOADED_VISITOR::COMMA
179
+ collector << Arel::Visitors::MSSQL::COMMA
337
180
  collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
338
181
  collector << ')'
339
182
  collector
340
183
  end
341
184
 
342
185
  def visit_ArelExtensions_Nodes_Trim o, collector
343
- # NOTE: in MSSQL's `blank`, o.right is the space char so we need to
344
- # account for it.
345
- if o.right && !/\A\s\Z/.match(o.right.expr)
346
- collector << 'dbo.TrimChar('
186
+ if o.right
187
+ collector << "REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(REPLACE("
347
188
  collector = visit o.left, collector
348
- collector << Arel::Visitors::MSSQL::COMMA
189
+ collector << ", ' ', '~'), "
349
190
  collector = visit o.right, collector
350
- collector << ')'
191
+ collector << ", ' '))), ' ', "
192
+ collector = visit o.right, collector
193
+ collector << "), '~', ' ')"
351
194
  else
352
195
  collector << "LTRIM(RTRIM("
353
196
  collector = visit o.left, collector
@@ -358,7 +201,7 @@ module ArelExtensions
358
201
 
359
202
  def visit_ArelExtensions_Nodes_Ltrim o, collector
360
203
  if o.right
361
- collector << 'REPLACE(REPLACE(LTRIM(REPLACE(REPLACE('
204
+ collector << "REPLACE(REPLACE(LTRIM(REPLACE(REPLACE("
362
205
  collector = visit o.left, collector
363
206
  collector << ", ' ', '~'), "
364
207
  collector = visit o.right, collector
@@ -366,16 +209,16 @@ module ArelExtensions
366
209
  collector = visit o.right, collector
367
210
  collector << "), '~', ' ')"
368
211
  else
369
- collector << 'LTRIM('
212
+ collector << "LTRIM("
370
213
  collector = visit o.left, collector
371
- collector << ')'
214
+ collector << ")"
372
215
  end
373
216
  collector
374
217
  end
375
218
 
376
219
  def visit_ArelExtensions_Nodes_Rtrim o, collector
377
220
  if o.right
378
- collector << 'REPLACE(REPLACE(RTRIM(REPLACE(REPLACE('
221
+ collector << "REPLACE(REPLACE(RTRIM(REPLACE(REPLACE("
379
222
  collector = visit o.left, collector
380
223
  collector << ", ' ', '~'), "
381
224
  collector = visit o.right, collector
@@ -383,9 +226,9 @@ module ArelExtensions
383
226
  collector = visit o.right, collector
384
227
  collector << "), '~', ' ')"
385
228
  else
386
- collector << 'RTRIM('
229
+ collector << "RTRIM("
387
230
  collector = visit o.left, collector
388
- collector << ')'
231
+ collector << ")"
389
232
  end
390
233
  collector
391
234
  end
@@ -399,102 +242,64 @@ module ArelExtensions
399
242
  end
400
243
 
401
244
  def visit_ArelExtensions_Nodes_Format o, collector
402
- visit_ArelExtensions_Nodes_FormattedDate o, collector
403
- end
404
-
405
- def visit_ArelExtensions_Nodes_FormattedDate o, collector
406
- f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, LOADED_VISITOR::DATE_FORMAT_DIRECTIVES)
407
- 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]
408
248
  collector << "CONVERT(VARCHAR(#{f.length})"
409
- collector << LOADED_VISITOR::COMMA
410
- if o.time_zone
411
- collector << 'CONVERT(datetime'
412
- collector << LOADED_VISITOR::COMMA
413
- collector << ' '
414
- end
249
+ collector << Arel::Visitors::MSSQL::COMMA
415
250
  collector = visit o.left, collector
416
- case o.time_zone
417
- when Hash
418
- src_tz, dst_tz = o.time_zone.first
419
- collector << ') AT TIME ZONE '
420
- collector = visit Arel.quoted(src_tz), collector
421
- collector << ' AT TIME ZONE '
422
- collector = visit Arel.quoted(dst_tz), collector
423
- when String
424
- collector << ') AT TIME ZONE '
425
- collector = visit Arel.quoted(o.time_zone), collector
426
- end
427
- collector << LOADED_VISITOR::COMMA
428
- collector << fmt.to_s
251
+ collector << Arel::Visitors::MSSQL::COMMA
252
+ collector << Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f].to_s
429
253
  collector << ')'
430
254
  collector
431
255
  else
432
- s = StringScanner.new o.iso_format
433
- collector << '('
434
- sep = ''
435
- while !s.eos?
436
- collector << sep
437
- sep = ' + '
438
- case
439
- when s.scan(LOADED_VISITOR::DATE_FORMAT_REGEX)
440
- dir = LOADED_VISITOR::DATE_FORMAT_DIRECTIVES[s.matched]
441
- fmt = LOADED_VISITOR::DATE_FORMAT_FORMAT[dir]
442
- date_name = LOADED_VISITOR::DATE_NAME.include?(s.matched)
443
- collector << 'LTRIM(RTRIM('
444
- collector << 'FORMAT(' if fmt
445
- collector << 'STR(' if !fmt && !date_name
446
- collector << (date_name ? 'DATENAME(' : 'DATEPART(')
447
- collector << dir
448
- collector << LOADED_VISITOR::COMMA
449
- if o.time_zone
450
- collector << 'CONVERT(datetime'
451
- collector << LOADED_VISITOR::COMMA
452
- 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
453
264
  end
454
- collector = visit o.left, collector
455
- case o.time_zone
456
- when Hash
457
- src_tz, dst_tz = o.time_zone.first.first, o.time_zone.first.second
458
- collector << ') AT TIME ZONE '
459
- collector = visit Arel.quoted(src_tz), collector
460
- collector << ' AT TIME ZONE '
461
- collector = visit Arel.quoted(dst_tz), collector
462
- when String
463
- collector << ') AT TIME ZONE '
464
- 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
465
276
  end
466
- collector << ')'
467
- collector << ')' if !fmt && !date_name
468
- collector << LOADED_VISITOR::COMMA << "'#{fmt}')" if fmt
469
- collector << '))'
470
- when s.scan(/^%%/)
471
- collector = visit Arel.quoted('%'), collector
472
- when s.scan(/[^%]+|./)
473
- collector = visit Arel.quoted(s.matched), collector
474
277
  end
475
- end
278
+ collector << ' + ' if t[i + 1]
279
+ }
280
+
476
281
  collector << ')'
477
282
  collector
478
283
  end
479
284
  end
480
285
 
481
286
  def visit_ArelExtensions_Nodes_Replace o, collector
482
- collector << 'REPLACE('
287
+ collector << "REPLACE("
483
288
  o.expressions.each_with_index { |arg, i|
484
- collector << LOADED_VISITOR::COMMA if i != 0
289
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
485
290
  collector = visit arg, collector
486
291
  }
487
- collector << ')'
292
+ collector << ")"
488
293
  collector
489
294
  end
490
295
 
491
296
  def visit_ArelExtensions_Nodes_FindInSet o, collector
492
- collector << 'dbo.FIND_IN_SET('
297
+ collector << "dbo.FIND_IN_SET("
493
298
  o.expressions.each_with_index { |arg, i|
494
- collector << LOADED_VISITOR::COMMA if i != 0
299
+ collector << Arel::Visitors::MSSQL::COMMA unless i == 0
495
300
  collector = visit arg, collector
496
301
  }
497
- collector << ')'
302
+ collector << ")"
498
303
  collector
499
304
  end
500
305
 
@@ -537,9 +342,9 @@ module ArelExtensions
537
342
  end
538
343
 
539
344
  def visit_ArelExtensions_Nodes_AiIMatches o, collector
540
- collector = visit o.left.collate(true, true), collector
345
+ collector = visit o.left.collate(true,true), collector
541
346
  collector << ' LIKE '
542
- collector = visit o.right.collate(true, true), collector
347
+ collector = visit o.right.collate(true,true), collector
543
348
  if o.escape
544
349
  collector << ' ESCAPE '
545
350
  visit o.escape, collector
@@ -577,34 +382,6 @@ module ArelExtensions
577
382
  collector
578
383
  end
579
384
 
580
- alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
581
- def visit_Arel_Nodes_As o, collector
582
- if o.left.is_a?(Arel::Nodes::Binary)
583
- collector << '('
584
- collector = visit o.left, collector
585
- collector << ')'
586
- else
587
- collector = visit o.left, collector
588
- end
589
- collector << ' AS '
590
- # Sometimes these values are already quoted, if they are, don't double quote it
591
- lft, rgt =
592
- if o.right.is_a?(Arel::Nodes::SqlLiteral)
593
- if AREL_VERSION >= V6 && o.right[0] != '[' && o.right[-1] != ']'
594
- # This is a lie, it's not about arel version, but SQL Server's (>= 2000).
595
- %w([ ])
596
- elsif ACTIVE_RECORD_VERSION < V8_1 && o.right[0] != '"' && o.right[-1] != '"'
597
- %w(" ")
598
- else
599
- []
600
- end
601
- end
602
- collector << lft if lft
603
- collector = visit o.right, collector
604
- collector << rgt if rgt
605
- collector
606
- end
607
-
608
385
  # SQL Server does not know about REGEXP
609
386
  def visit_Arel_Nodes_Regexp o, collector
610
387
  collector = visit o.left, collector
@@ -618,121 +395,108 @@ module ArelExtensions
618
395
  collector
619
396
  end
620
397
 
621
- def visit_Arel_Nodes_RollUp(o, collector)
622
- collector << "ROLLUP"
623
- grouping_array_or_grouping_element o, collector
624
- end
625
-
398
+ # TODO;
626
399
  def visit_ArelExtensions_Nodes_GroupConcat o, collector
627
- collector << '(STRING_AGG('
400
+ collector << "(STRING_AGG("
628
401
  collector = visit o.left, collector
629
402
  collector << Arel::Visitors::Oracle::COMMA
630
- sep = o.separator.is_a?(Arel::Nodes::Quoted) ? o.separator.expr : o.separator
631
- collector =
632
- if 'NULL' != sep
633
- visit o.separator, collector
634
- else
635
- visit Arel.quoted(','), collector
636
- end
637
- collector << ') WITHIN GROUP (ORDER BY '
638
- if o.order.present?
639
- o.order.each_with_index do |order, i|
640
- 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
641
412
  collector = visit order, collector
642
413
  end
643
414
  else
644
415
  collector = visit o.left, collector
645
416
  end
646
- collector << '))'
417
+ collector << "))"
647
418
  collector
648
419
  end
649
420
 
650
421
  def visit_ArelExtensions_Nodes_MD5 o, collector
651
422
  collector << "LOWER(CONVERT(NVARCHAR(32),HashBytes('MD5',CONVERT(VARCHAR,"
652
423
  collector = visit o.left, collector
653
- collector << ')),2))'
424
+ collector << ")),2))"
654
425
  collector
655
426
  end
656
427
 
657
428
  def visit_ArelExtensions_Nodes_Cast o, collector
658
- as_attr =
659
- case o.as_attr
660
- when :string
661
- 'varchar'
662
- when :time
663
- 'time'
664
- when :date
665
- 'date'
666
- when :datetime
667
- 'datetime'
668
- when :number, :decimal, :float
669
- 'decimal(10,6)'
670
- when :int
671
- collector << 'CAST(CAST('
672
- collector = visit o.left, collector
673
- collector << ' AS decimal(10,0)) AS int)'
674
- return collector
675
- when :binary
676
- 'binary'
677
- else
678
- o.as_attr.to_s
679
- end
680
- 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("
681
451
  collector = visit o.left, collector
682
- collector << ' AS '
683
- collector = visit Arel::Nodes::SqlLiteral.new(as_attr), collector
684
- collector << ')'
452
+ collector << " AS "
453
+ collector = visit as_attr, collector
454
+ collector << ")"
685
455
  collector
686
456
  end
687
457
 
688
458
  def visit_ArelExtensions_Nodes_FormattedNumber o, collector
689
459
  col = o.left.coalesce(0)
690
- locale = Arel.quoted(o.locale.tr('_', '-'))
691
- param = Arel.quoted("N#{o.precision}")
692
- 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).
693
463
  then('-').
694
464
  else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
695
465
  sign_length = o.flags.include?('+') || o.flags.include?(' ') ?
696
- Arel.quoted(1) :
697
- Arel.when(col < 0).then(1).else(0)
698
-
699
- number =
700
- if o.scientific_notation
701
- ArelExtensions::Nodes::Concat.new([
702
- Arel::Nodes::NamedFunction.new('FORMAT', [
703
- 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),
704
473
  param,
705
474
  locale
706
475
  ]),
707
476
  o.type,
708
- Arel::Nodes::NamedFunction.new('FORMAT', [
477
+ Arel::Nodes::NamedFunction.new('FORMAT',[
709
478
  col.abs.log10.floor,
710
- Arel.quoted('N0'),
479
+ Arel::Nodes.build_quoted('N0'),
711
480
  locale
712
481
  ])
713
482
  ])
714
- else
715
- Arel::Nodes::NamedFunction.new('FORMAT', [
716
- Arel.quoted(col.abs),
483
+ else
484
+ number = Arel::Nodes::NamedFunction.new('FORMAT',[
485
+ Arel::Nodes.build_quoted(col.abs),
717
486
  param,
718
487
  locale
719
488
  ])
720
- end
489
+ end
721
490
 
722
- repeated_char =
723
- if o.width == 0
724
- Arel.quoted('')
725
- else
726
- Arel
727
- .when(Arel.quoted(o.width).abs - (number.length + sign_length) > 0)
728
- .then(Arel.quoted(
729
- o.flags.include?('-') ? ' ' : (o.flags.include?('0') ? '0' : ' ')
730
- ).repeat(Arel.quoted(o.width).abs - (number.length + sign_length))
731
- )
732
- .else('')
733
- end
734
- before = !o.flags.include?('0') && !o.flags.include?('-') ? repeated_char : ''
735
- 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 : ''
736
500
  after = o.flags.include?('-') ? repeated_char : ''
737
501
  full_number =
738
502
  ArelExtensions::Nodes::Concat.new([
@@ -742,27 +506,27 @@ module ArelExtensions
742
506
  number,
743
507
  after
744
508
  ])
745
- 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
746
510
  collector
747
511
  end
748
512
 
749
513
  def visit_ArelExtensions_Nodes_Std o, collector
750
- collector << (o.unbiased_estimator ? 'STDEV(' : 'STDEVP(')
514
+ collector << (o.unbiased_estimator ? "STDEV(" : "STDEVP(")
751
515
  visit o.left, collector
752
- collector << ')'
516
+ collector << ")"
753
517
  collector
754
518
  end
755
519
 
756
520
  def visit_ArelExtensions_Nodes_Variance o, collector
757
- collector << (o.unbiased_estimator ? 'VAR(' : 'VARP(')
521
+ collector << (o.unbiased_estimator ? "VAR(" : "VARP(")
758
522
  visit o.left, collector
759
- collector << ')'
523
+ collector << ")"
760
524
  collector
761
525
  end
762
526
 
763
527
 
764
528
  def visit_ArelExtensions_Nodes_LevenshteinDistance o, collector
765
- collector << 'dbo.LEVENSHTEIN_DISTANCE('
529
+ collector << "dbo.LEVENSHTEIN_DISTANCE("
766
530
  collector = visit o.left, collector
767
531
  collector << Arel::Visitors::ToSql::COMMA
768
532
  collector = visit o.right, collector
@@ -771,30 +535,20 @@ module ArelExtensions
771
535
  end
772
536
 
773
537
 
774
- def visit_ArelExtensions_Nodes_JsonGet o, collector
538
+ def visit_ArelExtensions_Nodes_JsonGet o,collector
775
539
  collector << 'JSON_VALUE('
776
540
  collector = visit o.dict, collector
777
541
  collector << Arel::Visitors::MySQL::COMMA
778
542
  if o.key.is_a?(Integer)
779
543
  collector << "\"$[#{o.key}]\""
780
544
  else
781
- collector = visit Arel.quoted('$.') + o.key, collector
545
+ collector = visit Arel::Nodes.build_quoted('$.')+o.key, collector
782
546
  end
783
547
  collector << ')'
784
548
  collector
785
549
  end
786
550
 
787
- # Utilized by GroupingSet, Cube & RollUp visitors to
788
- # handle grouping aggregation semantics
789
- def grouping_array_or_grouping_element(o, collector)
790
- if o.expr.is_a? Array
791
- collector << "( "
792
- visit o.expr, collector
793
- collector << " )"
794
- else
795
- visit o.expr, collector
796
- end
797
- end
551
+
798
552
  end
799
553
  end
800
554
  end