arel_extensions 2.0.21 → 2.2.2
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -2
- data/.github/workflows/publish.yml +29 -0
- data/.github/workflows/release.yml +30 -0
- data/.github/workflows/ruby.yml +377 -80
- data/.gitignore +7 -6
- data/.rubocop.yml +62 -1
- data/CONTRIBUTING.md +102 -0
- data/Gemfile +2 -23
- data/NEWS.md +89 -0
- data/README.md +228 -84
- data/Rakefile +11 -4
- data/TODO +0 -1
- data/appveyor.yml +60 -22
- data/arel_extensions.gemspec +11 -12
- data/bin/build +15 -0
- data/bin/compose +6 -0
- data/bin/publish +8 -0
- data/dev/arelx.dockerfile +44 -0
- data/dev/compose.yaml +71 -0
- data/dev/postgres.dockerfile +5 -0
- data/dev/rbenv +189 -0
- data/gemfiles/rails3.gemfile +10 -10
- data/gemfiles/rails4_2.gemfile +38 -0
- data/gemfiles/rails5.gemfile +29 -0
- data/gemfiles/rails5_1_4.gemfile +13 -13
- data/gemfiles/rails5_2.gemfile +16 -14
- data/gemfiles/rails6.gemfile +18 -15
- data/gemfiles/rails6_1.gemfile +18 -15
- data/gemfiles/rails7.gemfile +33 -0
- data/gemfiles/rails7_1.gemfile +33 -0
- data/gemfiles/rails7_2.gemfile +33 -0
- data/gemspecs/arel_extensions-v1.gemspec +12 -13
- data/gemspecs/arel_extensions-v2.gemspec +11 -12
- data/init/mssql.sql +0 -0
- data/init/mysql.sql +0 -0
- data/init/oracle.sql +0 -0
- data/init/postgresql.sql +0 -0
- data/init/sqlite.sql +0 -0
- data/lib/arel_extensions/aliases.rb +14 -0
- data/lib/arel_extensions/attributes.rb +10 -2
- data/lib/arel_extensions/boolean_functions.rb +2 -4
- data/lib/arel_extensions/common_sql_functions.rb +12 -12
- data/lib/arel_extensions/comparators.rb +14 -14
- data/lib/arel_extensions/date_duration.rb +14 -9
- data/lib/arel_extensions/helpers.rb +62 -0
- data/lib/arel_extensions/insert_manager.rb +19 -17
- data/lib/arel_extensions/math.rb +48 -45
- data/lib/arel_extensions/math_functions.rb +18 -18
- data/lib/arel_extensions/nodes/abs.rb +0 -0
- data/lib/arel_extensions/nodes/aggregate_function.rb +0 -0
- data/lib/arel_extensions/nodes/blank.rb +1 -1
- data/lib/arel_extensions/nodes/case.rb +10 -12
- data/lib/arel_extensions/nodes/cast.rb +6 -6
- data/lib/arel_extensions/nodes/ceil.rb +0 -0
- data/lib/arel_extensions/nodes/change_case.rb +0 -0
- data/lib/arel_extensions/nodes/coalesce.rb +1 -1
- data/lib/arel_extensions/nodes/collate.rb +9 -9
- data/lib/arel_extensions/nodes/concat.rb +2 -2
- data/lib/arel_extensions/nodes/date_diff.rb +33 -14
- data/lib/arel_extensions/nodes/duration.rb +0 -0
- data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
- data/lib/arel_extensions/nodes/floor.rb +0 -0
- data/lib/arel_extensions/nodes/format.rb +3 -2
- data/lib/arel_extensions/nodes/formatted_date.rb +42 -0
- data/lib/arel_extensions/nodes/formatted_number.rb +2 -2
- data/lib/arel_extensions/nodes/function.rb +22 -26
- data/lib/arel_extensions/nodes/is_null.rb +0 -0
- data/lib/arel_extensions/nodes/json.rb +15 -9
- data/lib/arel_extensions/nodes/length.rb +6 -0
- data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
- data/lib/arel_extensions/nodes/locate.rb +1 -1
- data/lib/arel_extensions/nodes/log10.rb +0 -0
- data/lib/arel_extensions/nodes/matches.rb +1 -1
- data/lib/arel_extensions/nodes/md5.rb +0 -0
- data/lib/arel_extensions/nodes/power.rb +0 -0
- data/lib/arel_extensions/nodes/rand.rb +0 -0
- data/lib/arel_extensions/nodes/repeat.rb +2 -2
- data/lib/arel_extensions/nodes/replace.rb +2 -10
- data/lib/arel_extensions/nodes/rollup.rb +36 -0
- data/lib/arel_extensions/nodes/round.rb +0 -0
- data/lib/arel_extensions/nodes/select.rb +10 -0
- data/lib/arel_extensions/nodes/soundex.rb +2 -2
- data/lib/arel_extensions/nodes/std.rb +0 -0
- data/lib/arel_extensions/nodes/substring.rb +1 -1
- data/lib/arel_extensions/nodes/sum.rb +0 -0
- data/lib/arel_extensions/nodes/then.rb +1 -1
- data/lib/arel_extensions/nodes/trim.rb +2 -2
- data/lib/arel_extensions/nodes/union.rb +5 -5
- data/lib/arel_extensions/nodes/union_all.rb +4 -4
- data/lib/arel_extensions/nodes/wday.rb +0 -0
- data/lib/arel_extensions/nodes.rb +0 -0
- data/lib/arel_extensions/null_functions.rb +16 -0
- data/lib/arel_extensions/predications.rb +10 -10
- data/lib/arel_extensions/railtie.rb +1 -1
- data/lib/arel_extensions/set_functions.rb +3 -3
- data/lib/arel_extensions/string_functions.rb +19 -10
- data/lib/arel_extensions/tasks.rb +2 -2
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/convert_format.rb +0 -0
- data/lib/arel_extensions/visitors/ibm_db.rb +20 -20
- data/lib/arel_extensions/visitors/mssql.rb +394 -169
- data/lib/arel_extensions/visitors/mysql.rb +238 -151
- data/lib/arel_extensions/visitors/oracle.rb +170 -131
- data/lib/arel_extensions/visitors/oracle12.rb +16 -16
- data/lib/arel_extensions/visitors/postgresql.rb +170 -140
- data/lib/arel_extensions/visitors/sqlite.rb +88 -87
- data/lib/arel_extensions/visitors/to_sql.rb +185 -156
- data/lib/arel_extensions/visitors.rb +73 -60
- data/lib/arel_extensions.rb +173 -36
- data/test/arelx_test_helper.rb +49 -1
- data/test/database.yml +13 -7
- data/test/real_db_test.rb +101 -83
- data/test/support/fake_record.rb +8 -2
- data/test/test_comparators.rb +5 -5
- data/test/visitors/test_bulk_insert_oracle.rb +5 -5
- data/test/visitors/test_bulk_insert_sqlite.rb +5 -5
- data/test/visitors/test_bulk_insert_to_sql.rb +5 -5
- data/test/visitors/test_oracle.rb +14 -14
- data/test/visitors/test_to_sql.rb +121 -93
- data/test/with_ar/all_agnostic_test.rb +630 -320
- data/test/with_ar/insert_agnostic_test.rb +25 -18
- data/test/with_ar/test_bulk_sqlite.rb +11 -7
- data/test/with_ar/test_math_sqlite.rb +18 -14
- data/test/with_ar/test_string_mysql.rb +26 -22
- data/test/with_ar/test_string_sqlite.rb +26 -22
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +24 -26
- data/.travis/oracle/download.js +0 -152
- data/.travis/oracle/download.sh +0 -30
- data/.travis/oracle/download_ojdbc.js +0 -116
- data/.travis/oracle/install.sh +0 -34
- data/.travis/setup_accounts.sh +0 -9
- data/.travis/sqlite3/extension-functions.sh +0 -6
- data/.travis.yml +0 -193
- data/gemfiles/rails4.gemfile +0 -29
- data/gemfiles/rails5_0.gemfile +0 -29
@@ -2,97 +2,217 @@ module ArelExtensions
|
|
2
2
|
module Visitors
|
3
3
|
module MSSQL
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
36
156
|
|
37
157
|
# Math Functions
|
38
158
|
def visit_ArelExtensions_Nodes_Ceil o, collector
|
39
|
-
collector <<
|
159
|
+
collector << 'CEILING('
|
40
160
|
collector = visit o.expr, collector
|
41
|
-
collector <<
|
161
|
+
collector << ')'
|
42
162
|
collector
|
43
163
|
end
|
44
164
|
|
45
165
|
def visit_ArelExtensions_Nodes_Log10 o, collector
|
46
|
-
|
166
|
+
collector << 'LOG10('
|
47
167
|
o.expressions.each_with_index { |arg, i|
|
48
168
|
collector << Arel::Visitors::ToSql::COMMA if i != 0
|
49
169
|
collector = visit arg, collector
|
50
170
|
}
|
51
|
-
collector <<
|
171
|
+
collector << ')'
|
52
172
|
collector
|
53
173
|
end
|
54
174
|
|
55
175
|
def visit_ArelExtensions_Nodes_Power o, collector
|
56
|
-
collector <<
|
176
|
+
collector << 'POWER('
|
57
177
|
o.expressions.each_with_index { |arg, i|
|
58
178
|
collector << Arel::Visitors::ToSql::COMMA if i != 0
|
59
179
|
collector = visit arg, collector
|
60
180
|
}
|
61
|
-
collector <<
|
181
|
+
collector << ')'
|
62
182
|
collector
|
63
183
|
end
|
64
184
|
|
65
185
|
def visit_ArelExtensions_Nodes_IsNull o, collector
|
66
|
-
collector <<
|
186
|
+
collector << '('
|
67
187
|
collector = visit o.expr, collector
|
68
|
-
collector <<
|
188
|
+
collector << ' IS NULL)'
|
69
189
|
collector
|
70
190
|
end
|
71
191
|
|
72
192
|
def visit_ArelExtensions_Nodes_IsNotNull o, collector
|
73
|
-
|
193
|
+
collector << '('
|
74
194
|
collector = visit o.expr, collector
|
75
|
-
collector <<
|
195
|
+
collector << ' IS NOT NULL)'
|
76
196
|
collector
|
77
197
|
end
|
78
198
|
|
79
199
|
def visit_ArelExtensions_Nodes_Concat o, collector
|
80
|
-
collector <<
|
200
|
+
collector << 'CONCAT('
|
81
201
|
o.expressions.each_with_index { |arg, i|
|
82
|
-
collector <<
|
202
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
83
203
|
collector = visit arg, collector
|
84
204
|
}
|
85
|
-
collector <<
|
205
|
+
collector << ')'
|
86
206
|
collector
|
87
207
|
end
|
88
208
|
|
89
209
|
def visit_ArelExtensions_Nodes_Repeat o, collector
|
90
|
-
collector <<
|
210
|
+
collector << 'REPLICATE('
|
91
211
|
o.expressions.each_with_index { |arg, i|
|
92
212
|
collector << Arel::Visitors::ToSql::COMMA if i != 0
|
93
213
|
collector = visit arg, collector
|
94
214
|
}
|
95
|
-
collector <<
|
215
|
+
collector << ')'
|
96
216
|
collector
|
97
217
|
end
|
98
218
|
|
@@ -102,38 +222,38 @@ module ArelExtensions
|
|
102
222
|
case o.right_node_type
|
103
223
|
when :ruby_date, :ruby_time, :date, :datetime, :time
|
104
224
|
collector << case o.left_node_type
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
collector <<
|
225
|
+
when :ruby_time, :datetime, :time then 'DATEDIFF(second'
|
226
|
+
else 'DATEDIFF(day'
|
227
|
+
end
|
228
|
+
collector << LOADED_VISITOR::COMMA
|
109
229
|
collector = visit o.right, collector
|
110
|
-
collector <<
|
230
|
+
collector << LOADED_VISITOR::COMMA
|
111
231
|
collector = visit o.left, collector
|
112
232
|
collector << ')'
|
113
233
|
else
|
114
234
|
da = ArelExtensions::Nodes::DateAdd.new([])
|
115
|
-
collector <<
|
235
|
+
collector << 'DATEADD('
|
116
236
|
collector = visit da.mssql_datepart(o.right), collector
|
117
|
-
collector <<
|
118
|
-
collector <<
|
237
|
+
collector << LOADED_VISITOR::COMMA
|
238
|
+
collector << '-('
|
119
239
|
collector = visit da.mssql_value(o.right), collector
|
120
|
-
collector <<
|
121
|
-
collector <<
|
240
|
+
collector << ')'
|
241
|
+
collector << LOADED_VISITOR::COMMA
|
122
242
|
collector = visit o.left, collector
|
123
|
-
collector <<
|
243
|
+
collector << ')'
|
124
244
|
collector
|
125
245
|
end
|
126
246
|
collector
|
127
247
|
end
|
128
248
|
|
129
249
|
def visit_ArelExtensions_Nodes_DateAdd o, collector
|
130
|
-
collector <<
|
250
|
+
collector << 'DATEADD('
|
131
251
|
collector = visit o.mssql_datepart(o.right), collector
|
132
|
-
collector <<
|
252
|
+
collector << LOADED_VISITOR::COMMA
|
133
253
|
collector = visit o.mssql_value(o.right), collector
|
134
|
-
collector <<
|
254
|
+
collector << LOADED_VISITOR::COMMA
|
135
255
|
collector = visit o.left, collector
|
136
|
-
collector <<
|
256
|
+
collector << ')'
|
137
257
|
collector
|
138
258
|
end
|
139
259
|
|
@@ -142,68 +262,77 @@ module ArelExtensions
|
|
142
262
|
collector = visit o.right, collector
|
143
263
|
else
|
144
264
|
left = o.left.end_with?('i') ? o.left[0..-2] : o.left
|
145
|
-
conv = [
|
265
|
+
conv = %w[h mn s].include?(o.left)
|
146
266
|
collector << 'DATEPART('
|
147
|
-
collector <<
|
148
|
-
collector <<
|
267
|
+
collector << LOADED_VISITOR::DATE_MAPPING[left]
|
268
|
+
collector << LOADED_VISITOR::COMMA
|
149
269
|
collector << 'CONVERT(datetime,' if conv
|
150
270
|
collector = visit o.right, collector
|
151
271
|
collector << ')' if conv
|
152
|
-
collector <<
|
272
|
+
collector << ')'
|
153
273
|
end
|
154
274
|
collector
|
155
275
|
end
|
156
276
|
|
157
277
|
def visit_ArelExtensions_Nodes_Length o, collector
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
162
291
|
end
|
163
292
|
|
164
293
|
def visit_ArelExtensions_Nodes_Round o, collector
|
165
|
-
collector <<
|
294
|
+
collector << 'ROUND('
|
166
295
|
o.expressions.each_with_index { |arg, i|
|
167
|
-
collector <<
|
296
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
168
297
|
collector = visit arg, collector
|
169
298
|
}
|
170
299
|
if o.expressions.length == 1
|
171
|
-
collector <<
|
172
|
-
collector <<
|
300
|
+
collector << LOADED_VISITOR::COMMA
|
301
|
+
collector << '0'
|
173
302
|
end
|
174
|
-
collector <<
|
303
|
+
collector << ')'
|
175
304
|
collector
|
176
305
|
end
|
177
306
|
|
178
307
|
def visit_ArelExtensions_Nodes_Locate o, collector
|
179
|
-
collector <<
|
308
|
+
collector << 'CHARINDEX('
|
180
309
|
collector = visit o.right, collector
|
181
|
-
collector <<
|
310
|
+
collector << LOADED_VISITOR::COMMA
|
182
311
|
collector = visit o.left, collector
|
183
|
-
collector <<
|
312
|
+
collector << ')'
|
184
313
|
collector
|
185
314
|
end
|
186
315
|
|
187
316
|
def visit_ArelExtensions_Nodes_Substring o, collector
|
188
317
|
collector << 'SUBSTRING('
|
189
318
|
collector = visit o.expressions[0], collector
|
190
|
-
collector <<
|
319
|
+
collector << LOADED_VISITOR::COMMA
|
191
320
|
collector = visit o.expressions[1], collector
|
192
|
-
collector <<
|
321
|
+
collector << LOADED_VISITOR::COMMA
|
193
322
|
collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
|
194
323
|
collector << ')'
|
195
324
|
collector
|
196
325
|
end
|
197
326
|
|
198
327
|
def visit_ArelExtensions_Nodes_Trim o, collector
|
199
|
-
|
200
|
-
|
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('
|
201
332
|
collector = visit o.left, collector
|
202
|
-
collector <<
|
203
|
-
collector = visit o.right, collector
|
204
|
-
collector << ", ' '))), ' ', "
|
333
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
205
334
|
collector = visit o.right, collector
|
206
|
-
collector <<
|
335
|
+
collector << ')'
|
207
336
|
else
|
208
337
|
collector << "LTRIM(RTRIM("
|
209
338
|
collector = visit o.left, collector
|
@@ -214,7 +343,7 @@ module ArelExtensions
|
|
214
343
|
|
215
344
|
def visit_ArelExtensions_Nodes_Ltrim o, collector
|
216
345
|
if o.right
|
217
|
-
collector <<
|
346
|
+
collector << 'REPLACE(REPLACE(LTRIM(REPLACE(REPLACE('
|
218
347
|
collector = visit o.left, collector
|
219
348
|
collector << ", ' ', '~'), "
|
220
349
|
collector = visit o.right, collector
|
@@ -222,16 +351,16 @@ module ArelExtensions
|
|
222
351
|
collector = visit o.right, collector
|
223
352
|
collector << "), '~', ' ')"
|
224
353
|
else
|
225
|
-
collector <<
|
354
|
+
collector << 'LTRIM('
|
226
355
|
collector = visit o.left, collector
|
227
|
-
collector <<
|
356
|
+
collector << ')'
|
228
357
|
end
|
229
358
|
collector
|
230
359
|
end
|
231
360
|
|
232
361
|
def visit_ArelExtensions_Nodes_Rtrim o, collector
|
233
362
|
if o.right
|
234
|
-
collector <<
|
363
|
+
collector << 'REPLACE(REPLACE(RTRIM(REPLACE(REPLACE('
|
235
364
|
collector = visit o.left, collector
|
236
365
|
collector << ", ' ', '~'), "
|
237
366
|
collector = visit o.right, collector
|
@@ -239,9 +368,9 @@ module ArelExtensions
|
|
239
368
|
collector = visit o.right, collector
|
240
369
|
collector << "), '~', ' ')"
|
241
370
|
else
|
242
|
-
collector <<
|
371
|
+
collector << 'RTRIM('
|
243
372
|
collector = visit o.left, collector
|
244
|
-
collector <<
|
373
|
+
collector << ')'
|
245
374
|
end
|
246
375
|
collector
|
247
376
|
end
|
@@ -255,32 +384,78 @@ module ArelExtensions
|
|
255
384
|
end
|
256
385
|
|
257
386
|
def visit_ArelExtensions_Nodes_Format o, collector
|
258
|
-
|
259
|
-
|
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]
|
260
393
|
collector << "CONVERT(VARCHAR(#{f.length})"
|
261
|
-
collector <<
|
394
|
+
collector << LOADED_VISITOR::COMMA
|
395
|
+
if o.time_zone
|
396
|
+
collector << 'CONVERT(datetime'
|
397
|
+
collector << LOADED_VISITOR::COMMA
|
398
|
+
collector << ' '
|
399
|
+
end
|
262
400
|
collector = visit o.left, collector
|
263
|
-
|
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
|
264
413
|
collector << fmt.to_s
|
265
414
|
collector << ')'
|
266
415
|
collector
|
267
416
|
else
|
268
417
|
s = StringScanner.new o.iso_format
|
269
|
-
collector <<
|
418
|
+
collector << '('
|
270
419
|
sep = ''
|
271
420
|
while !s.eos?
|
272
421
|
collector << sep
|
273
422
|
sep = ' + '
|
274
423
|
case
|
275
|
-
when s.scan(
|
276
|
-
dir =
|
277
|
-
|
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(')
|
278
432
|
collector << dir
|
279
|
-
collector <<
|
433
|
+
collector << LOADED_VISITOR::COMMA
|
434
|
+
if o.time_zone
|
435
|
+
collector << 'CONVERT(datetime'
|
436
|
+
collector << LOADED_VISITOR::COMMA
|
437
|
+
collector << ' '
|
438
|
+
end
|
280
439
|
collector = visit o.left, collector
|
281
|
-
|
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
|
450
|
+
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
|
282
457
|
when s.scan(/[^%]+|./)
|
283
|
-
collector = visit Arel
|
458
|
+
collector = visit Arel.quoted(s.matched), collector
|
284
459
|
end
|
285
460
|
end
|
286
461
|
collector << ')'
|
@@ -289,22 +464,22 @@ module ArelExtensions
|
|
289
464
|
end
|
290
465
|
|
291
466
|
def visit_ArelExtensions_Nodes_Replace o, collector
|
292
|
-
collector <<
|
467
|
+
collector << 'REPLACE('
|
293
468
|
o.expressions.each_with_index { |arg, i|
|
294
|
-
collector <<
|
469
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
295
470
|
collector = visit arg, collector
|
296
471
|
}
|
297
|
-
collector <<
|
472
|
+
collector << ')'
|
298
473
|
collector
|
299
474
|
end
|
300
475
|
|
301
476
|
def visit_ArelExtensions_Nodes_FindInSet o, collector
|
302
|
-
collector <<
|
477
|
+
collector << 'dbo.FIND_IN_SET('
|
303
478
|
o.expressions.each_with_index { |arg, i|
|
304
|
-
collector <<
|
479
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
305
480
|
collector = visit arg, collector
|
306
481
|
}
|
307
|
-
collector <<
|
482
|
+
collector << ')'
|
308
483
|
collector
|
309
484
|
end
|
310
485
|
|
@@ -347,9 +522,9 @@ module ArelExtensions
|
|
347
522
|
end
|
348
523
|
|
349
524
|
def visit_ArelExtensions_Nodes_AiIMatches o, collector
|
350
|
-
collector = visit o.left.collate(true,true), collector
|
525
|
+
collector = visit o.left.collate(true, true), collector
|
351
526
|
collector << ' LIKE '
|
352
|
-
collector = visit o.right.collate(true,true), collector
|
527
|
+
collector = visit o.right.collate(true, true), collector
|
353
528
|
if o.escape
|
354
529
|
collector << ' ESCAPE '
|
355
530
|
visit o.escape, collector
|
@@ -387,6 +562,34 @@ module ArelExtensions
|
|
387
562
|
collector
|
388
563
|
end
|
389
564
|
|
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
|
+
|
390
593
|
# SQL Server does not know about REGEXP
|
391
594
|
def visit_Arel_Nodes_Regexp o, collector
|
392
595
|
collector = visit o.left, collector
|
@@ -400,110 +603,121 @@ module ArelExtensions
|
|
400
603
|
collector
|
401
604
|
end
|
402
605
|
|
606
|
+
def visit_Arel_Nodes_RollUp(o, collector)
|
607
|
+
collector << "ROLLUP"
|
608
|
+
grouping_array_or_grouping_element o, collector
|
609
|
+
end
|
610
|
+
|
403
611
|
# TODO;
|
404
612
|
def visit_ArelExtensions_Nodes_GroupConcat o, collector
|
405
|
-
collector <<
|
613
|
+
collector << '(STRING_AGG('
|
406
614
|
collector = visit o.left, collector
|
407
615
|
collector << Arel::Visitors::Oracle::COMMA
|
408
616
|
collector =
|
409
617
|
if o.separator && o.separator != 'NULL'
|
410
618
|
visit o.separator, collector
|
411
619
|
else
|
412
|
-
visit Arel
|
620
|
+
visit Arel.quoted(','), collector
|
413
621
|
end
|
414
|
-
collector <<
|
622
|
+
collector << ') WITHIN GROUP (ORDER BY '
|
415
623
|
if o.order.present?
|
416
|
-
o.order.each_with_index do |order,i|
|
624
|
+
o.order.each_with_index do |order, i|
|
417
625
|
collector << Arel::Visitors::Oracle::COMMA if i != 0
|
418
626
|
collector = visit order, collector
|
419
627
|
end
|
420
628
|
else
|
421
629
|
collector = visit o.left, collector
|
422
630
|
end
|
423
|
-
collector <<
|
631
|
+
collector << '))'
|
424
632
|
collector
|
425
633
|
end
|
426
634
|
|
427
635
|
def visit_ArelExtensions_Nodes_MD5 o, collector
|
428
636
|
collector << "LOWER(CONVERT(NVARCHAR(32),HashBytes('MD5',CONVERT(VARCHAR,"
|
429
637
|
collector = visit o.left, collector
|
430
|
-
collector <<
|
638
|
+
collector << ')),2))'
|
431
639
|
collector
|
432
640
|
end
|
433
641
|
|
434
642
|
def visit_ArelExtensions_Nodes_Cast o, collector
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
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('
|
457
666
|
collector = visit o.left, collector
|
458
|
-
collector <<
|
459
|
-
collector = visit as_attr, collector
|
460
|
-
collector <<
|
667
|
+
collector << ' AS '
|
668
|
+
collector = visit Arel::Nodes::SqlLiteral.new(as_attr), collector
|
669
|
+
collector << ')'
|
461
670
|
collector
|
462
671
|
end
|
463
672
|
|
464
673
|
def visit_ArelExtensions_Nodes_FormattedNumber o, collector
|
465
674
|
col = o.left.coalesce(0)
|
466
|
-
locale = Arel
|
467
|
-
param = Arel
|
468
|
-
sign =
|
675
|
+
locale = Arel.quoted(o.locale.tr('_', '-'))
|
676
|
+
param = Arel.quoted("N#{o.precision}")
|
677
|
+
sign = Arel.when(col < 0).
|
469
678
|
then('-').
|
470
679
|
else(o.flags.include?('+') ? '+' : (o.flags.include?(' ') ? ' ' : ''))
|
471
680
|
sign_length = o.flags.include?('+') || o.flags.include?(' ') ?
|
472
|
-
Arel
|
473
|
-
|
681
|
+
Arel.quoted(1) :
|
682
|
+
Arel.when(col < 0).then(1).else(0)
|
474
683
|
|
475
684
|
number =
|
476
685
|
if o.scientific_notation
|
477
686
|
ArelExtensions::Nodes::Concat.new([
|
478
|
-
Arel::Nodes::NamedFunction.new('FORMAT',[
|
479
|
-
col.abs/Arel
|
687
|
+
Arel::Nodes::NamedFunction.new('FORMAT', [
|
688
|
+
col.abs / Arel.quoted(10).pow(col.abs.log10.floor),
|
480
689
|
param,
|
481
690
|
locale
|
482
691
|
]),
|
483
692
|
o.type,
|
484
|
-
Arel::Nodes::NamedFunction.new('FORMAT',[
|
693
|
+
Arel::Nodes::NamedFunction.new('FORMAT', [
|
485
694
|
col.abs.log10.floor,
|
486
|
-
Arel
|
695
|
+
Arel.quoted('N0'),
|
487
696
|
locale
|
488
697
|
])
|
489
698
|
])
|
490
699
|
else
|
491
|
-
Arel::Nodes::NamedFunction.new('FORMAT',[
|
492
|
-
Arel
|
700
|
+
Arel::Nodes::NamedFunction.new('FORMAT', [
|
701
|
+
Arel.quoted(col.abs),
|
493
702
|
param,
|
494
703
|
locale
|
495
704
|
])
|
496
705
|
end
|
497
706
|
|
498
|
-
repeated_char =
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
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 : ''
|
507
721
|
after = o.flags.include?('-') ? repeated_char : ''
|
508
722
|
full_number =
|
509
723
|
ArelExtensions::Nodes::Concat.new([
|
@@ -513,27 +727,27 @@ module ArelExtensions
|
|
513
727
|
number,
|
514
728
|
after
|
515
729
|
])
|
516
|
-
collector = visit ArelExtensions::Nodes::Concat.new([Arel
|
730
|
+
collector = visit ArelExtensions::Nodes::Concat.new([Arel.quoted(o.prefix), full_number, Arel.quoted(o.suffix)]), collector
|
517
731
|
collector
|
518
732
|
end
|
519
733
|
|
520
734
|
def visit_ArelExtensions_Nodes_Std o, collector
|
521
|
-
collector << (o.unbiased_estimator ?
|
735
|
+
collector << (o.unbiased_estimator ? 'STDEV(' : 'STDEVP(')
|
522
736
|
visit o.left, collector
|
523
|
-
collector <<
|
737
|
+
collector << ')'
|
524
738
|
collector
|
525
739
|
end
|
526
740
|
|
527
741
|
def visit_ArelExtensions_Nodes_Variance o, collector
|
528
|
-
collector << (o.unbiased_estimator ?
|
742
|
+
collector << (o.unbiased_estimator ? 'VAR(' : 'VARP(')
|
529
743
|
visit o.left, collector
|
530
|
-
collector <<
|
744
|
+
collector << ')'
|
531
745
|
collector
|
532
746
|
end
|
533
747
|
|
534
748
|
|
535
749
|
def visit_ArelExtensions_Nodes_LevenshteinDistance o, collector
|
536
|
-
collector <<
|
750
|
+
collector << 'dbo.LEVENSHTEIN_DISTANCE('
|
537
751
|
collector = visit o.left, collector
|
538
752
|
collector << Arel::Visitors::ToSql::COMMA
|
539
753
|
collector = visit o.right, collector
|
@@ -542,19 +756,30 @@ module ArelExtensions
|
|
542
756
|
end
|
543
757
|
|
544
758
|
|
545
|
-
def visit_ArelExtensions_Nodes_JsonGet o,collector
|
759
|
+
def visit_ArelExtensions_Nodes_JsonGet o, collector
|
546
760
|
collector << 'JSON_VALUE('
|
547
761
|
collector = visit o.dict, collector
|
548
762
|
collector << Arel::Visitors::MySQL::COMMA
|
549
763
|
if o.key.is_a?(Integer)
|
550
764
|
collector << "\"$[#{o.key}]\""
|
551
765
|
else
|
552
|
-
collector = visit Arel
|
766
|
+
collector = visit Arel.quoted('$.') + o.key, collector
|
553
767
|
end
|
554
768
|
collector << ')'
|
555
769
|
collector
|
556
770
|
end
|
557
771
|
|
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
|
558
783
|
end
|
559
784
|
end
|
560
785
|
end
|