arel_extensions 2.1.0 → 2.1.1
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/.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
|