arel_extensions 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +258 -52
- data/Gemfile +8 -8
- data/README.md +44 -0
- data/gemfiles/rails5_2.gemfile +3 -3
- data/gemfiles/rails6.gemfile +3 -4
- data/gemfiles/rails6_1.gemfile +1 -2
- data/gemfiles/rails7.gemfile +5 -13
- data/lib/arel_extensions/date_duration.rb +2 -2
- data/lib/arel_extensions/helpers.rb +48 -0
- data/lib/arel_extensions/math.rb +17 -27
- data/lib/arel_extensions/nodes/case.rb +3 -5
- data/lib/arel_extensions/nodes/date_diff.rb +23 -4
- data/lib/arel_extensions/nodes/format.rb +3 -2
- data/lib/arel_extensions/nodes/function.rb +1 -7
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/mssql.rb +109 -51
- data/lib/arel_extensions/visitors/mysql.rb +6 -0
- data/lib/arel_extensions/visitors/oracle.rb +6 -1
- data/lib/arel_extensions/visitors/postgresql.rb +9 -5
- data/lib/arel_extensions/visitors/sqlite.rb +6 -3
- data/lib/arel_extensions.rb +11 -1
- data/test/arelx_test_helper.rb +45 -0
- data/test/database.yml +8 -2
- data/test/support/fake_record.rb +1 -1
- data/test/visitors/test_to_sql.rb +21 -0
- data/test/with_ar/all_agnostic_test.rb +66 -3
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +3 -3
- data/appveyor.yml +0 -44
data/lib/arel_extensions/math.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'arel_extensions/helpers'
|
2
|
+
|
1
3
|
require 'arel_extensions/nodes'
|
2
4
|
require 'arel_extensions/nodes/function'
|
3
5
|
require 'arel_extensions/nodes/concat'
|
@@ -38,21 +40,17 @@ module ArelExtensions
|
|
38
40
|
when Arel::Nodes::Function
|
39
41
|
Arel.grouping(Arel::Nodes::Addition.new self, other)
|
40
42
|
else
|
41
|
-
|
42
|
-
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
43
|
-
rescue Exception
|
44
|
-
col = nil
|
45
|
-
end
|
43
|
+
col = self.respond_to?(:relation)? ArelExtensions::column_of(self.relation.table_name, self.name.to_s) : nil
|
46
44
|
if (!col) # if the column doesn't exist in the database
|
47
|
-
Arel.grouping(Arel::Nodes::Addition.new(self, other))
|
45
|
+
Arel.grouping(Arel::Nodes::Addition.new(self, Arel::Nodes.build_quoted(other)))
|
48
46
|
else
|
49
47
|
arg = col.type
|
50
48
|
if arg == :integer || (!arg)
|
51
49
|
other = other.to_i if other.is_a?(String)
|
52
|
-
Arel.grouping(Arel::Nodes::Addition.new self, other)
|
50
|
+
Arel.grouping(Arel::Nodes::Addition.new self, Arel::Nodes.build_quoted(other))
|
53
51
|
elsif arg == :decimal || arg == :float
|
54
52
|
other = Arel.sql(other) if other.is_a?(String) # Arel should accept Float & BigDecimal!
|
55
|
-
Arel.grouping(Arel::Nodes::Addition.new self, other)
|
53
|
+
Arel.grouping(Arel::Nodes::Addition.new self, Arel::Nodes.build_quoted(other))
|
56
54
|
elsif arg == :datetime || arg == :date
|
57
55
|
ArelExtensions::Nodes::DateAdd.new [self, other]
|
58
56
|
elsif arg == :string || arg == :text
|
@@ -68,41 +66,33 @@ module ArelExtensions
|
|
68
66
|
case self
|
69
67
|
when Arel::Nodes::Grouping
|
70
68
|
if self.expr.left.is_a?(Date) || self.expr.left.is_a?(DateTime)
|
71
|
-
Arel.grouping(ArelExtensions::Nodes::DateSub.new [self, other])
|
69
|
+
Arel.grouping(ArelExtensions::Nodes::DateSub.new [self, Arel::Nodes.build_quoted(other)])
|
72
70
|
else
|
73
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
71
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
74
72
|
end
|
75
73
|
when ArelExtensions::Nodes::Function, ArelExtensions::Nodes::Case
|
76
74
|
case self.return_type
|
77
75
|
when :string, :text # ???
|
78
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other)) # ??
|
76
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other))) # ??
|
79
77
|
when :integer, :decimal, :float, :number
|
80
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
78
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
81
79
|
when :date, :datetime
|
82
|
-
ArelExtensions::Nodes::DateSub.new [self, other]
|
80
|
+
ArelExtensions::Nodes::DateSub.new [self, Arel::Nodes.build_quoted(other)]
|
83
81
|
else
|
84
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
82
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
85
83
|
end
|
86
84
|
when Arel::Nodes::Function
|
87
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
85
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
88
86
|
else
|
89
|
-
|
90
|
-
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
91
|
-
rescue Exception
|
92
|
-
col = nil
|
93
|
-
end
|
87
|
+
col = ArelExtensions::column_of(self.relation.table_name, self.name.to_s)
|
94
88
|
if (!col) # if the column doesn't exist in the database
|
95
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
89
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
96
90
|
else
|
97
91
|
arg = col.type
|
98
92
|
if (arg == :date || arg == :datetime)
|
99
93
|
case other
|
100
94
|
when Arel::Attributes::Attribute
|
101
|
-
|
102
|
-
col2 = Arel::Table.engine.connection.schema_cache.columns_hash(other.relation.table_name)[other.name.to_s]
|
103
|
-
rescue Exception
|
104
|
-
col2 = nil
|
105
|
-
end
|
95
|
+
col2 = ArelExtensions::column_of(other.relation.table_name, other.name.to_s)
|
106
96
|
if (!col2) # if the column doesn't exist in the database
|
107
97
|
ArelExtensions::Nodes::DateSub.new [self, other]
|
108
98
|
else
|
@@ -127,7 +117,7 @@ module ArelExtensions
|
|
127
117
|
when String
|
128
118
|
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
|
129
119
|
else
|
130
|
-
Arel.grouping(Arel::Nodes::Subtraction.new(self, other))
|
120
|
+
Arel.grouping(Arel::Nodes::Subtraction.new(self, Arel::Nodes.build_quoted(other)))
|
131
121
|
end
|
132
122
|
end
|
133
123
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'arel_extensions/helpers'
|
2
|
+
|
1
3
|
module ArelExtensions
|
2
4
|
module Nodes
|
3
5
|
if Gem::Version.new(Arel::VERSION) < Gem::Version.new("7.1.0")
|
@@ -57,11 +59,7 @@ module ArelExtensions
|
|
57
59
|
when Date, DateTime,Time
|
58
60
|
:datetime
|
59
61
|
when Arel::Attributes::Attribute
|
60
|
-
|
61
|
-
Arel::Table.engine.connection.schema_cache.columns_hash(obj.relation.table_name)[obj.name.to_s].type
|
62
|
-
rescue Exception
|
63
|
-
:string
|
64
|
-
end
|
62
|
+
ArelExtensions::column_of(obj.relation.table_name, obj.name.to_s)&.type || :string
|
65
63
|
else
|
66
64
|
:string
|
67
65
|
end
|
@@ -117,7 +117,16 @@ module ArelExtensions
|
|
117
117
|
if @date_type == :date
|
118
118
|
v.to_i / (24*3600)
|
119
119
|
elsif @date_type == :datetime
|
120
|
-
v.
|
120
|
+
if v.parts.size == 1
|
121
|
+
# first entry in the dict v.parts; one of [:years, :months, :weeks, :days, :hours, :minutes, :seconds]
|
122
|
+
# | the value
|
123
|
+
# | |
|
124
|
+
# | |
|
125
|
+
# v v
|
126
|
+
v.parts.first.second
|
127
|
+
else
|
128
|
+
v.to_i
|
129
|
+
end
|
121
130
|
end
|
122
131
|
else
|
123
132
|
v
|
@@ -130,7 +139,17 @@ module ArelExtensions
|
|
130
139
|
if @date_type == :date
|
131
140
|
Arel.sql('day')
|
132
141
|
elsif @date_type == :datetime
|
133
|
-
|
142
|
+
res = if v.parts.size == 1
|
143
|
+
# first entry in the dict v.parts; one of [:years, :months, :weeks, :days, :hours, :minutes, :seconds]
|
144
|
+
# | the key
|
145
|
+
# | | convert symbol to string
|
146
|
+
# | | | remove the plural suffix `s`
|
147
|
+
# v v v v
|
148
|
+
v.parts.first.first.to_s[0..-2]
|
149
|
+
else
|
150
|
+
'second'
|
151
|
+
end
|
152
|
+
Arel.sql(res)
|
134
153
|
end
|
135
154
|
else
|
136
155
|
if ArelExtensions::Nodes::Duration === v
|
@@ -141,9 +160,9 @@ module ArelExtensions
|
|
141
160
|
when 'h','mn','s'
|
142
161
|
Arel.sql('second')
|
143
162
|
when /i\z/
|
144
|
-
Arel.sql(
|
163
|
+
Arel.sql(ArelExtensions::Visitors::MSSQL::LOADED_VISITOR::DATE_MAPPING[v.left[0..-2]])
|
145
164
|
else
|
146
|
-
Arel.sql(
|
165
|
+
Arel.sql(ArelExtensions::Visitors::MSSQL::LOADED_VISITOR::DATE_MAPPING[v.left])
|
147
166
|
end
|
148
167
|
end
|
149
168
|
end
|
@@ -5,11 +5,12 @@ module ArelExtensions
|
|
5
5
|
class Format < Function
|
6
6
|
RETURN_TYPE = :string
|
7
7
|
|
8
|
-
attr_accessor :col_type, :iso_format
|
8
|
+
attr_accessor :col_type, :iso_format, :time_zone
|
9
9
|
|
10
10
|
def initialize expr
|
11
|
-
col = expr
|
11
|
+
col = expr[0]
|
12
12
|
@iso_format = convert_format(expr[1])
|
13
|
+
@time_zone = Arel::Nodes.build_quoted(expr[2]) if expr[2]
|
13
14
|
@col_type = type_of_attribute(col)
|
14
15
|
super [col, convert_to_string_node(@iso_format)]
|
15
16
|
end
|
@@ -51,13 +51,7 @@ module ArelExtensions
|
|
51
51
|
def type_of_attribute(att)
|
52
52
|
case att
|
53
53
|
when Arel::Attributes::Attribute
|
54
|
-
|
55
|
-
if Arel::Table.engine.connection.tables.include? att.relation.table_name
|
56
|
-
Arel::Table.engine.connection.schema_cache.columns_hash(att.relation.table_name)[att.name.to_s].type
|
57
|
-
end
|
58
|
-
rescue
|
59
|
-
att
|
60
|
-
end
|
54
|
+
ArelExtensions::column_of(att.relation.table_name, att.name.to_s)&.type || att
|
61
55
|
when ArelExtensions::Nodes::Function
|
62
56
|
att.return_type
|
63
57
|
# else
|
@@ -2,27 +2,37 @@ module ArelExtensions
|
|
2
2
|
module Visitors
|
3
3
|
module MSSQL
|
4
4
|
|
5
|
-
Arel::Visitors
|
5
|
+
mssql_class = Arel::Visitors.constants.select { |c|
|
6
|
+
Arel::Visitors.const_get(c).is_a?(Class) && %i[MSSQL SQLServer].include?(c)
|
7
|
+
}.first
|
8
|
+
|
9
|
+
LOADED_VISITOR = Arel::Visitors.const_get(mssql_class) || Arel::Visitors.const_get('MSSQL')
|
10
|
+
|
11
|
+
LOADED_VISITOR::DATE_MAPPING = {
|
6
12
|
'd' => 'day', 'm' => 'month', 'y' => 'year', 'wd' => 'weekday', 'w' => 'week', 'h' => 'hour', 'mn' => 'minute', 's' => 'second'
|
7
13
|
}.freeze
|
8
14
|
|
9
|
-
|
15
|
+
LOADED_VISITOR::DATE_FORMAT_DIRECTIVES = {
|
10
16
|
'%Y' => 'YYYY', '%C' => '', '%y' => 'YY', '%m' => 'MM', '%B' => '', '%b' => '', '%^b' => '', # year, month
|
11
17
|
'%d' => 'DD', '%e' => '', '%j' => '', '%w' => 'dw', '%A' => '', # day, weekday
|
12
18
|
'%H' => 'hh', '%k' => '', '%I' => '', '%l' => '', '%P' => '', '%p' => '', # hours
|
13
19
|
'%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
|
14
20
|
}.freeze
|
15
21
|
|
16
|
-
|
22
|
+
LOADED_VISITOR::DATE_FORMAT_FORMAT = {
|
23
|
+
'YY' => '0#', 'MM' => '0#', 'DD' => '0#', 'hh' => '0#', 'mi' => '0#', 'ss' => '0#'
|
24
|
+
}
|
25
|
+
|
26
|
+
LOADED_VISITOR::DATE_FORMAT_REGEX =
|
17
27
|
Regexp.new(
|
18
|
-
|
28
|
+
LOADED_VISITOR::DATE_FORMAT_DIRECTIVES
|
19
29
|
.keys
|
20
30
|
.map{|k| Regexp.escape(k)}
|
21
31
|
.join('|')
|
22
32
|
).freeze
|
23
33
|
|
24
34
|
# TODO; all others... http://www.sql-server-helper.com/tips/date-formats.aspx
|
25
|
-
|
35
|
+
LOADED_VISITOR::DATE_CONVERT_FORMATS = {
|
26
36
|
'YYYY-MM-DD' => 120,
|
27
37
|
'YY-MM-DD' => 120,
|
28
38
|
'MM/DD/YYYY' => 101,
|
@@ -79,7 +89,7 @@ module ArelExtensions
|
|
79
89
|
def visit_ArelExtensions_Nodes_Concat o, collector
|
80
90
|
collector << "CONCAT("
|
81
91
|
o.expressions.each_with_index { |arg, i|
|
82
|
-
collector <<
|
92
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
83
93
|
collector = visit arg, collector
|
84
94
|
}
|
85
95
|
collector << ")"
|
@@ -102,23 +112,23 @@ module ArelExtensions
|
|
102
112
|
case o.right_node_type
|
103
113
|
when :ruby_date, :ruby_time, :date, :datetime, :time
|
104
114
|
collector << case o.left_node_type
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
collector <<
|
115
|
+
when :ruby_time, :datetime, :time then 'DATEDIFF(second'
|
116
|
+
else 'DATEDIFF(day'
|
117
|
+
end
|
118
|
+
collector << LOADED_VISITOR::COMMA
|
109
119
|
collector = visit o.right, collector
|
110
|
-
collector <<
|
120
|
+
collector << LOADED_VISITOR::COMMA
|
111
121
|
collector = visit o.left, collector
|
112
122
|
collector << ')'
|
113
123
|
else
|
114
124
|
da = ArelExtensions::Nodes::DateAdd.new([])
|
115
125
|
collector << "DATEADD("
|
116
126
|
collector = visit da.mssql_datepart(o.right), collector
|
117
|
-
collector <<
|
127
|
+
collector << LOADED_VISITOR::COMMA
|
118
128
|
collector << "-("
|
119
129
|
collector = visit da.mssql_value(o.right), collector
|
120
130
|
collector << ")"
|
121
|
-
collector <<
|
131
|
+
collector << LOADED_VISITOR::COMMA
|
122
132
|
collector = visit o.left, collector
|
123
133
|
collector << ")"
|
124
134
|
collector
|
@@ -129,9 +139,9 @@ module ArelExtensions
|
|
129
139
|
def visit_ArelExtensions_Nodes_DateAdd o, collector
|
130
140
|
collector << "DATEADD("
|
131
141
|
collector = visit o.mssql_datepart(o.right), collector
|
132
|
-
collector <<
|
142
|
+
collector << LOADED_VISITOR::COMMA
|
133
143
|
collector = visit o.mssql_value(o.right), collector
|
134
|
-
collector <<
|
144
|
+
collector << LOADED_VISITOR::COMMA
|
135
145
|
collector = visit o.left, collector
|
136
146
|
collector << ")"
|
137
147
|
collector
|
@@ -144,8 +154,8 @@ module ArelExtensions
|
|
144
154
|
left = o.left.end_with?('i') ? o.left[0..-2] : o.left
|
145
155
|
conv = ['h', 'mn', 's'].include?(o.left)
|
146
156
|
collector << 'DATEPART('
|
147
|
-
collector <<
|
148
|
-
collector <<
|
157
|
+
collector << LOADED_VISITOR::DATE_MAPPING[left]
|
158
|
+
collector << LOADED_VISITOR::COMMA
|
149
159
|
collector << 'CONVERT(datetime,' if conv
|
150
160
|
collector = visit o.right, collector
|
151
161
|
collector << ')' if conv
|
@@ -155,20 +165,29 @@ module ArelExtensions
|
|
155
165
|
end
|
156
166
|
|
157
167
|
def visit_ArelExtensions_Nodes_Length o, collector
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
168
|
+
if o.bytewise
|
169
|
+
collector << "(DATALENGTH("
|
170
|
+
collector = visit o.expr, collector
|
171
|
+
collector << ") / ISNULL(NULLIF(DATALENGTH(LEFT(COALESCE("
|
172
|
+
collector = visit o.expr, collector
|
173
|
+
collector << ", '#' ), 1 )), 0), 1))"
|
174
|
+
collector
|
175
|
+
else
|
176
|
+
collector << "LEN("
|
177
|
+
collector = visit o.expr, collector
|
178
|
+
collector << ")"
|
179
|
+
collector
|
180
|
+
end
|
162
181
|
end
|
163
182
|
|
164
183
|
def visit_ArelExtensions_Nodes_Round o, collector
|
165
184
|
collector << "ROUND("
|
166
185
|
o.expressions.each_with_index { |arg, i|
|
167
|
-
collector <<
|
186
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
168
187
|
collector = visit arg, collector
|
169
188
|
}
|
170
189
|
if o.expressions.length == 1
|
171
|
-
collector <<
|
190
|
+
collector << LOADED_VISITOR::COMMA
|
172
191
|
collector << "0"
|
173
192
|
end
|
174
193
|
collector << ")"
|
@@ -178,7 +197,7 @@ module ArelExtensions
|
|
178
197
|
def visit_ArelExtensions_Nodes_Locate o, collector
|
179
198
|
collector << "CHARINDEX("
|
180
199
|
collector = visit o.right, collector
|
181
|
-
collector <<
|
200
|
+
collector << LOADED_VISITOR::COMMA
|
182
201
|
collector = visit o.left, collector
|
183
202
|
collector << ")"
|
184
203
|
collector
|
@@ -187,28 +206,20 @@ module ArelExtensions
|
|
187
206
|
def visit_ArelExtensions_Nodes_Substring o, collector
|
188
207
|
collector << 'SUBSTRING('
|
189
208
|
collector = visit o.expressions[0], collector
|
190
|
-
collector <<
|
209
|
+
collector << LOADED_VISITOR::COMMA
|
191
210
|
collector = visit o.expressions[1], collector
|
192
|
-
collector <<
|
211
|
+
collector << LOADED_VISITOR::COMMA
|
193
212
|
collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
|
194
213
|
collector << ')'
|
195
214
|
collector
|
196
215
|
end
|
197
216
|
|
198
217
|
def visit_ArelExtensions_Nodes_Trim o, collector
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
collector << ", ' '))), ' ', "
|
205
|
-
collector = visit o.right, collector
|
206
|
-
collector << "), '~', ' ')"
|
207
|
-
else
|
208
|
-
collector << "LTRIM(RTRIM("
|
209
|
-
collector = visit o.left, collector
|
210
|
-
collector << "))"
|
211
|
-
end
|
218
|
+
collector << 'TRIM( '
|
219
|
+
collector = visit o.right, collector
|
220
|
+
collector << " FROM "
|
221
|
+
collector = visit o.left, collector
|
222
|
+
collector << ")"
|
212
223
|
collector
|
213
224
|
end
|
214
225
|
|
@@ -255,12 +266,21 @@ module ArelExtensions
|
|
255
266
|
end
|
256
267
|
|
257
268
|
def visit_ArelExtensions_Nodes_Format o, collector
|
258
|
-
f = ArelExtensions::Visitors::strftime_to_format(o.iso_format,
|
259
|
-
if fmt =
|
269
|
+
f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, LOADED_VISITOR::DATE_FORMAT_DIRECTIVES)
|
270
|
+
if fmt = LOADED_VISITOR::DATE_CONVERT_FORMATS[f]
|
260
271
|
collector << "CONVERT(VARCHAR(#{f.length})"
|
261
|
-
collector <<
|
272
|
+
collector << LOADED_VISITOR::COMMA
|
273
|
+
if o.time_zone
|
274
|
+
collector << 'CONVERT(datetime'
|
275
|
+
collector << LOADED_VISITOR::COMMA
|
276
|
+
collector << ' '
|
277
|
+
end
|
262
278
|
collector = visit o.left, collector
|
263
|
-
|
279
|
+
if o.time_zone
|
280
|
+
collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
|
281
|
+
collector = visit o.time_zone, collector
|
282
|
+
end
|
283
|
+
collector << LOADED_VISITOR::COMMA
|
264
284
|
collector << fmt.to_s
|
265
285
|
collector << ')'
|
266
286
|
collector
|
@@ -272,13 +292,31 @@ module ArelExtensions
|
|
272
292
|
collector << sep
|
273
293
|
sep = ' + '
|
274
294
|
case
|
275
|
-
when s.scan(
|
276
|
-
dir =
|
277
|
-
|
295
|
+
when s.scan(LOADED_VISITOR::DATE_FORMAT_REGEX)
|
296
|
+
dir = LOADED_VISITOR::DATE_FORMAT_DIRECTIVES[s.matched]
|
297
|
+
fmt = LOADED_VISITOR::DATE_FORMAT_FORMAT[dir]
|
298
|
+
collector << 'TRIM('
|
299
|
+
collector << 'FORMAT(' if fmt
|
300
|
+
collector << 'STR(' if !fmt
|
301
|
+
collector << 'DATEPART('
|
278
302
|
collector << dir
|
279
|
-
collector <<
|
303
|
+
collector << LOADED_VISITOR::COMMA
|
304
|
+
if o.time_zone
|
305
|
+
collector << 'CONVERT(datetime'
|
306
|
+
collector << LOADED_VISITOR::COMMA
|
307
|
+
collector << ' '
|
308
|
+
end
|
280
309
|
collector = visit o.left, collector
|
281
|
-
|
310
|
+
if o.time_zone
|
311
|
+
collector << ") AT TIME ZONE 'UTC' AT TIME ZONE "
|
312
|
+
collector = visit o.time_zone, collector
|
313
|
+
end
|
314
|
+
collector << ')'
|
315
|
+
collector << ')' if !fmt
|
316
|
+
collector << LOADED_VISITOR::COMMA << "'#{fmt}')" if fmt
|
317
|
+
collector << ')'
|
318
|
+
when s.scan(/^%%/)
|
319
|
+
collector = visit Arel::Nodes.build_quoted('%'), collector
|
282
320
|
when s.scan(/[^%]+|./)
|
283
321
|
collector = visit Arel::Nodes.build_quoted(s.matched), collector
|
284
322
|
end
|
@@ -291,7 +329,7 @@ module ArelExtensions
|
|
291
329
|
def visit_ArelExtensions_Nodes_Replace o, collector
|
292
330
|
collector << "REPLACE("
|
293
331
|
o.expressions.each_with_index { |arg, i|
|
294
|
-
collector <<
|
332
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
295
333
|
collector = visit arg, collector
|
296
334
|
}
|
297
335
|
collector << ")"
|
@@ -301,7 +339,7 @@ module ArelExtensions
|
|
301
339
|
def visit_ArelExtensions_Nodes_FindInSet o, collector
|
302
340
|
collector << "dbo.FIND_IN_SET("
|
303
341
|
o.expressions.each_with_index { |arg, i|
|
304
|
-
collector <<
|
342
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
305
343
|
collector = visit arg, collector
|
306
344
|
}
|
307
345
|
collector << ")"
|
@@ -387,6 +425,27 @@ module ArelExtensions
|
|
387
425
|
collector
|
388
426
|
end
|
389
427
|
|
428
|
+
alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
|
429
|
+
def visit_Arel_Nodes_As o, collector
|
430
|
+
if o.left.is_a?(Arel::Nodes::Binary)
|
431
|
+
collector << '('
|
432
|
+
collector = visit o.left, collector
|
433
|
+
collector << ')'
|
434
|
+
else
|
435
|
+
collector = visit o.left, collector
|
436
|
+
end
|
437
|
+
collector << " AS "
|
438
|
+
|
439
|
+
# sometimes these values are already quoted, if they are, don't double quote it
|
440
|
+
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
|
441
|
+
|
442
|
+
collector << '"' if quote
|
443
|
+
collector = visit o.right, collector
|
444
|
+
collector << '"' if quote
|
445
|
+
|
446
|
+
collector
|
447
|
+
end
|
448
|
+
|
390
449
|
# SQL Server does not know about REGEXP
|
391
450
|
def visit_Arel_Nodes_Regexp o, collector
|
392
451
|
collector = visit o.left, collector
|
@@ -554,7 +613,6 @@ module ArelExtensions
|
|
554
613
|
collector << ')'
|
555
614
|
collector
|
556
615
|
end
|
557
|
-
|
558
616
|
end
|
559
617
|
end
|
560
618
|
end
|
@@ -209,7 +209,13 @@ module ArelExtensions
|
|
209
209
|
when :date, :datetime, :time
|
210
210
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
211
211
|
collector << "DATE_FORMAT("
|
212
|
+
collector << "CONVERT_TZ(" if o.time_zone
|
212
213
|
collector = visit o.left, collector
|
214
|
+
if o.time_zone
|
215
|
+
collector << COMMA << "'UTC'" << COMMA
|
216
|
+
collector = visit o.time_zone, collector
|
217
|
+
collector << ')'
|
218
|
+
end
|
213
219
|
collector << COMMA
|
214
220
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
215
221
|
collector << ")"
|
@@ -447,7 +447,12 @@ module ArelExtensions
|
|
447
447
|
def visit_ArelExtensions_Nodes_Format o, collector
|
448
448
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
449
449
|
collector << "TO_CHAR("
|
450
|
+
collector << "CAST(" if o.time_zone
|
450
451
|
collector = visit o.left, collector
|
452
|
+
if o.time_zone
|
453
|
+
collector << " as timestamp) at time zone "
|
454
|
+
collector = visit o.time_zone, collector
|
455
|
+
end
|
451
456
|
collector << COMMA
|
452
457
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
453
458
|
collector << ")"
|
@@ -517,7 +522,7 @@ module ArelExtensions
|
|
517
522
|
if element.is_a?(Time)
|
518
523
|
ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
|
519
524
|
elsif element.is_a?(Arel::Attributes::Attribute)
|
520
|
-
col =
|
525
|
+
col = ArelExtensions::column_of(element.relation.table_name, element.name.to_s)
|
521
526
|
if col && (col.type == :time)
|
522
527
|
ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
|
523
528
|
else
|
@@ -90,11 +90,11 @@ module ArelExtensions
|
|
90
90
|
|
91
91
|
# sometimes these values are already quoted, if they are, don't double quote it
|
92
92
|
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
|
93
|
-
|
93
|
+
|
94
94
|
collector << '"' if quote
|
95
95
|
collector = visit o.right, collector
|
96
96
|
collector << '"' if quote
|
97
|
-
|
97
|
+
|
98
98
|
collector
|
99
99
|
end
|
100
100
|
|
@@ -176,6 +176,10 @@ module ArelExtensions
|
|
176
176
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
177
177
|
collector << "TO_CHAR("
|
178
178
|
collector = visit o.left, collector
|
179
|
+
if o.time_zone
|
180
|
+
collector << " AT TIME ZONE "
|
181
|
+
collector = visit o.time_zone, collector
|
182
|
+
end
|
179
183
|
collector << COMMA
|
180
184
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
181
185
|
collector << ")"
|
@@ -289,9 +293,9 @@ module ArelExtensions
|
|
289
293
|
return collector
|
290
294
|
end
|
291
295
|
end
|
292
|
-
collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM "
|
296
|
+
collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM CAST("
|
293
297
|
collector = visit o.right, collector
|
294
|
-
collector << ")"
|
298
|
+
collector << " AS TIMESTAMP WITH TIME ZONE))"
|
295
299
|
collector << " * (INTERVAL '1' #{interval})" if interval && o.with_interval
|
296
300
|
collector
|
297
301
|
end
|
@@ -374,7 +378,7 @@ module ArelExtensions
|
|
374
378
|
when :number, :decimal, :float
|
375
379
|
Arel::Nodes::SqlLiteral.new('numeric')
|
376
380
|
when :datetime
|
377
|
-
Arel::Nodes::SqlLiteral.new('timestamp')
|
381
|
+
Arel::Nodes::SqlLiteral.new('timestamp with time zone')
|
378
382
|
when :date
|
379
383
|
Arel::Nodes::SqlLiteral.new('date')
|
380
384
|
when :binary
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'arel_extensions/helpers'
|
2
|
+
|
1
3
|
module ArelExtensions
|
2
4
|
module Visitors
|
3
5
|
class Arel::Visitors::SQLite
|
@@ -327,7 +329,7 @@ module ArelExtensions
|
|
327
329
|
if element.is_a?(Time)
|
328
330
|
return Arel::Nodes::NamedFunction.new('STRFTIME',[element, '%H:%M:%S'])
|
329
331
|
elsif element.is_a?(Arel::Attributes::Attribute)
|
330
|
-
col =
|
332
|
+
col = ArelExtensions::column_of(element.relation.table_name, element.name.to_s)
|
331
333
|
if col && (col.type == :time)
|
332
334
|
return Arel::Nodes::NamedFunction.new('STRFTIME',[element, '%H:%M:%S'])
|
333
335
|
else
|
@@ -379,9 +381,10 @@ module ArelExtensions
|
|
379
381
|
else
|
380
382
|
collector = visit o.left, collector
|
381
383
|
end
|
382
|
-
|
384
|
+
sep = o.right.size > 1 && o.right[0] == '"' && o.right[-1] == '"' ? '' : '"'
|
385
|
+
collector << " AS #{sep}"
|
383
386
|
collector = visit o.right, collector
|
384
|
-
collector << "
|
387
|
+
collector << "#{sep}"
|
385
388
|
collector
|
386
389
|
end
|
387
390
|
|
data/lib/arel_extensions.rb
CHANGED
@@ -77,7 +77,17 @@ require 'arel_extensions/nodes/soundex'
|
|
77
77
|
require 'arel_extensions/nodes/cast'
|
78
78
|
require 'arel_extensions/nodes/json'
|
79
79
|
|
80
|
-
|
80
|
+
# It seems like the code in lib/arel_extensions/visitors.rb that is supposed
|
81
|
+
# to inject ArelExtension is not enough. Different versions of the sqlserver
|
82
|
+
# adapter behave differently. It doesn't always proc, so we added this for
|
83
|
+
# coverage.
|
84
|
+
if defined?(Arel::Visitors::SQLServer)
|
85
|
+
Arel::Visitors.const_set('MSSQL', Arel::Visitors::SQLServer)
|
86
|
+
require 'arel_extensions/visitors/mssql'
|
87
|
+
class Arel::Visitors::SQLServer
|
88
|
+
include ArelExtensions::Visitors::MSSQL
|
89
|
+
end
|
90
|
+
end
|
81
91
|
|
82
92
|
module Arel
|
83
93
|
def self.rand
|