arel_extensions 1.3.0 → 1.3.3
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 +65 -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 +126 -53
- data/lib/arel_extensions/visitors/mysql.rb +15 -0
- data/lib/arel_extensions/visitors/oracle.rb +14 -1
- data/lib/arel_extensions/visitors/postgresql.rb +20 -7
- data/lib/arel_extensions/visitors/sqlite.rb +6 -3
- data/lib/arel_extensions.rb +11 -1
- data/test/arelx_test_helper.rb +45 -1
- 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 +103 -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 = 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,38 @@ 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
|
-
'%
|
12
|
-
'%
|
17
|
+
'%V' => 'iso_week', '%G' => '', # ISO week number and year of week
|
18
|
+
'%d' => 'DD', '%e' => '', '%j' => '', '%w' => 'dw', '%A' => '', # day, weekday
|
19
|
+
'%H' => 'hh', '%k' => '', '%I' => '', '%l' => '', '%P' => '', '%p' => '', # hours
|
13
20
|
'%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
|
14
21
|
}.freeze
|
15
22
|
|
16
|
-
|
23
|
+
LOADED_VISITOR::DATE_FORMAT_FORMAT = {
|
24
|
+
'YY' => '0#', 'MM' => '0#', 'DD' => '0#', 'hh' => '0#', 'mi' => '0#', 'ss' => '0#', 'iso_week' => '0#'
|
25
|
+
}
|
26
|
+
|
27
|
+
LOADED_VISITOR::DATE_FORMAT_REGEX =
|
17
28
|
Regexp.new(
|
18
|
-
|
29
|
+
LOADED_VISITOR::DATE_FORMAT_DIRECTIVES
|
19
30
|
.keys
|
20
31
|
.map{|k| Regexp.escape(k)}
|
21
32
|
.join('|')
|
22
33
|
).freeze
|
23
34
|
|
24
35
|
# TODO; all others... http://www.sql-server-helper.com/tips/date-formats.aspx
|
25
|
-
|
36
|
+
LOADED_VISITOR::DATE_CONVERT_FORMATS = {
|
26
37
|
'YYYY-MM-DD' => 120,
|
27
38
|
'YY-MM-DD' => 120,
|
28
39
|
'MM/DD/YYYY' => 101,
|
@@ -79,7 +90,7 @@ module ArelExtensions
|
|
79
90
|
def visit_ArelExtensions_Nodes_Concat o, collector
|
80
91
|
collector << "CONCAT("
|
81
92
|
o.expressions.each_with_index { |arg, i|
|
82
|
-
collector <<
|
93
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
83
94
|
collector = visit arg, collector
|
84
95
|
}
|
85
96
|
collector << ")"
|
@@ -102,23 +113,23 @@ module ArelExtensions
|
|
102
113
|
case o.right_node_type
|
103
114
|
when :ruby_date, :ruby_time, :date, :datetime, :time
|
104
115
|
collector << case o.left_node_type
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
collector <<
|
116
|
+
when :ruby_time, :datetime, :time then 'DATEDIFF(second'
|
117
|
+
else 'DATEDIFF(day'
|
118
|
+
end
|
119
|
+
collector << LOADED_VISITOR::COMMA
|
109
120
|
collector = visit o.right, collector
|
110
|
-
collector <<
|
121
|
+
collector << LOADED_VISITOR::COMMA
|
111
122
|
collector = visit o.left, collector
|
112
123
|
collector << ')'
|
113
124
|
else
|
114
125
|
da = ArelExtensions::Nodes::DateAdd.new([])
|
115
126
|
collector << "DATEADD("
|
116
127
|
collector = visit da.mssql_datepart(o.right), collector
|
117
|
-
collector <<
|
128
|
+
collector << LOADED_VISITOR::COMMA
|
118
129
|
collector << "-("
|
119
130
|
collector = visit da.mssql_value(o.right), collector
|
120
131
|
collector << ")"
|
121
|
-
collector <<
|
132
|
+
collector << LOADED_VISITOR::COMMA
|
122
133
|
collector = visit o.left, collector
|
123
134
|
collector << ")"
|
124
135
|
collector
|
@@ -129,9 +140,9 @@ module ArelExtensions
|
|
129
140
|
def visit_ArelExtensions_Nodes_DateAdd o, collector
|
130
141
|
collector << "DATEADD("
|
131
142
|
collector = visit o.mssql_datepart(o.right), collector
|
132
|
-
collector <<
|
143
|
+
collector << LOADED_VISITOR::COMMA
|
133
144
|
collector = visit o.mssql_value(o.right), collector
|
134
|
-
collector <<
|
145
|
+
collector << LOADED_VISITOR::COMMA
|
135
146
|
collector = visit o.left, collector
|
136
147
|
collector << ")"
|
137
148
|
collector
|
@@ -144,8 +155,8 @@ module ArelExtensions
|
|
144
155
|
left = o.left.end_with?('i') ? o.left[0..-2] : o.left
|
145
156
|
conv = ['h', 'mn', 's'].include?(o.left)
|
146
157
|
collector << 'DATEPART('
|
147
|
-
collector <<
|
148
|
-
collector <<
|
158
|
+
collector << LOADED_VISITOR::DATE_MAPPING[left]
|
159
|
+
collector << LOADED_VISITOR::COMMA
|
149
160
|
collector << 'CONVERT(datetime,' if conv
|
150
161
|
collector = visit o.right, collector
|
151
162
|
collector << ')' if conv
|
@@ -155,20 +166,29 @@ module ArelExtensions
|
|
155
166
|
end
|
156
167
|
|
157
168
|
def visit_ArelExtensions_Nodes_Length o, collector
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
169
|
+
if o.bytewise
|
170
|
+
collector << "(DATALENGTH("
|
171
|
+
collector = visit o.expr, collector
|
172
|
+
collector << ") / ISNULL(NULLIF(DATALENGTH(LEFT(COALESCE("
|
173
|
+
collector = visit o.expr, collector
|
174
|
+
collector << ", '#' ), 1 )), 0), 1))"
|
175
|
+
collector
|
176
|
+
else
|
177
|
+
collector << "LEN("
|
178
|
+
collector = visit o.expr, collector
|
179
|
+
collector << ")"
|
180
|
+
collector
|
181
|
+
end
|
162
182
|
end
|
163
183
|
|
164
184
|
def visit_ArelExtensions_Nodes_Round o, collector
|
165
185
|
collector << "ROUND("
|
166
186
|
o.expressions.each_with_index { |arg, i|
|
167
|
-
collector <<
|
187
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
168
188
|
collector = visit arg, collector
|
169
189
|
}
|
170
190
|
if o.expressions.length == 1
|
171
|
-
collector <<
|
191
|
+
collector << LOADED_VISITOR::COMMA
|
172
192
|
collector << "0"
|
173
193
|
end
|
174
194
|
collector << ")"
|
@@ -178,7 +198,7 @@ module ArelExtensions
|
|
178
198
|
def visit_ArelExtensions_Nodes_Locate o, collector
|
179
199
|
collector << "CHARINDEX("
|
180
200
|
collector = visit o.right, collector
|
181
|
-
collector <<
|
201
|
+
collector << LOADED_VISITOR::COMMA
|
182
202
|
collector = visit o.left, collector
|
183
203
|
collector << ")"
|
184
204
|
collector
|
@@ -187,28 +207,20 @@ module ArelExtensions
|
|
187
207
|
def visit_ArelExtensions_Nodes_Substring o, collector
|
188
208
|
collector << 'SUBSTRING('
|
189
209
|
collector = visit o.expressions[0], collector
|
190
|
-
collector <<
|
210
|
+
collector << LOADED_VISITOR::COMMA
|
191
211
|
collector = visit o.expressions[1], collector
|
192
|
-
collector <<
|
212
|
+
collector << LOADED_VISITOR::COMMA
|
193
213
|
collector = o.expressions[2] ? visit(o.expressions[2], collector) : visit(o.expressions[0].length, collector)
|
194
214
|
collector << ')'
|
195
215
|
collector
|
196
216
|
end
|
197
217
|
|
198
218
|
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
|
219
|
+
collector << 'TRIM( '
|
220
|
+
collector = visit o.right, collector
|
221
|
+
collector << " FROM "
|
222
|
+
collector = visit o.left, collector
|
223
|
+
collector << ")"
|
212
224
|
collector
|
213
225
|
end
|
214
226
|
|
@@ -255,12 +267,28 @@ module ArelExtensions
|
|
255
267
|
end
|
256
268
|
|
257
269
|
def visit_ArelExtensions_Nodes_Format o, collector
|
258
|
-
f = ArelExtensions::Visitors::strftime_to_format(o.iso_format,
|
259
|
-
if fmt =
|
270
|
+
f = ArelExtensions::Visitors::strftime_to_format(o.iso_format, LOADED_VISITOR::DATE_FORMAT_DIRECTIVES)
|
271
|
+
if fmt = LOADED_VISITOR::DATE_CONVERT_FORMATS[f]
|
260
272
|
collector << "CONVERT(VARCHAR(#{f.length})"
|
261
|
-
collector <<
|
273
|
+
collector << LOADED_VISITOR::COMMA
|
274
|
+
if o.time_zone
|
275
|
+
collector << 'CONVERT(datetime'
|
276
|
+
collector << LOADED_VISITOR::COMMA
|
277
|
+
collector << ' '
|
278
|
+
end
|
262
279
|
collector = visit o.left, collector
|
263
|
-
|
280
|
+
case o.time_zone
|
281
|
+
when Hash
|
282
|
+
src_tz, dst_tz = o.time_zone.first
|
283
|
+
collector << ') AT TIME ZONE '
|
284
|
+
collector = visit Arel::Nodes.build_quoted(src_tz), collector
|
285
|
+
collector << ' AT TIME ZONE '
|
286
|
+
collector = visit Arel::Nodes.build_quoted(dst_tz), collector
|
287
|
+
when String
|
288
|
+
collector << ') AT TIME ZONE '
|
289
|
+
collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
|
290
|
+
end
|
291
|
+
collector << LOADED_VISITOR::COMMA
|
264
292
|
collector << fmt.to_s
|
265
293
|
collector << ')'
|
266
294
|
collector
|
@@ -272,13 +300,38 @@ module ArelExtensions
|
|
272
300
|
collector << sep
|
273
301
|
sep = ' + '
|
274
302
|
case
|
275
|
-
when s.scan(
|
276
|
-
dir =
|
277
|
-
|
303
|
+
when s.scan(LOADED_VISITOR::DATE_FORMAT_REGEX)
|
304
|
+
dir = LOADED_VISITOR::DATE_FORMAT_DIRECTIVES[s.matched]
|
305
|
+
fmt = LOADED_VISITOR::DATE_FORMAT_FORMAT[dir]
|
306
|
+
collector << 'TRIM('
|
307
|
+
collector << 'FORMAT(' if fmt
|
308
|
+
collector << 'STR(' if !fmt
|
309
|
+
collector << 'DATEPART('
|
278
310
|
collector << dir
|
279
|
-
collector <<
|
311
|
+
collector << LOADED_VISITOR::COMMA
|
312
|
+
if o.time_zone
|
313
|
+
collector << 'CONVERT(datetime'
|
314
|
+
collector << LOADED_VISITOR::COMMA
|
315
|
+
collector << ' '
|
316
|
+
end
|
280
317
|
collector = visit o.left, collector
|
281
|
-
|
318
|
+
case o.time_zone
|
319
|
+
when Hash
|
320
|
+
src_tz, dst_tz = o.time_zone.first.first, o.time_zone.first.second
|
321
|
+
collector << ") AT TIME ZONE "
|
322
|
+
collector = visit Arel::Nodes.build_quoted(src_tz), collector
|
323
|
+
collector << " AT TIME ZONE "
|
324
|
+
collector = visit Arel::Nodes.build_quoted(dst_tz), collector
|
325
|
+
when String
|
326
|
+
collector << ") AT TIME ZONE "
|
327
|
+
collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
|
328
|
+
end
|
329
|
+
collector << ')'
|
330
|
+
collector << ')' if !fmt
|
331
|
+
collector << LOADED_VISITOR::COMMA << "'#{fmt}')" if fmt
|
332
|
+
collector << ')'
|
333
|
+
when s.scan(/^%%/)
|
334
|
+
collector = visit Arel::Nodes.build_quoted('%'), collector
|
282
335
|
when s.scan(/[^%]+|./)
|
283
336
|
collector = visit Arel::Nodes.build_quoted(s.matched), collector
|
284
337
|
end
|
@@ -291,7 +344,7 @@ module ArelExtensions
|
|
291
344
|
def visit_ArelExtensions_Nodes_Replace o, collector
|
292
345
|
collector << "REPLACE("
|
293
346
|
o.expressions.each_with_index { |arg, i|
|
294
|
-
collector <<
|
347
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
295
348
|
collector = visit arg, collector
|
296
349
|
}
|
297
350
|
collector << ")"
|
@@ -301,7 +354,7 @@ module ArelExtensions
|
|
301
354
|
def visit_ArelExtensions_Nodes_FindInSet o, collector
|
302
355
|
collector << "dbo.FIND_IN_SET("
|
303
356
|
o.expressions.each_with_index { |arg, i|
|
304
|
-
collector <<
|
357
|
+
collector << LOADED_VISITOR::COMMA if i != 0
|
305
358
|
collector = visit arg, collector
|
306
359
|
}
|
307
360
|
collector << ")"
|
@@ -387,6 +440,27 @@ module ArelExtensions
|
|
387
440
|
collector
|
388
441
|
end
|
389
442
|
|
443
|
+
alias_method(:old_visit_Arel_Nodes_As, :visit_Arel_Nodes_As) rescue nil
|
444
|
+
def visit_Arel_Nodes_As o, collector
|
445
|
+
if o.left.is_a?(Arel::Nodes::Binary)
|
446
|
+
collector << '('
|
447
|
+
collector = visit o.left, collector
|
448
|
+
collector << ')'
|
449
|
+
else
|
450
|
+
collector = visit o.left, collector
|
451
|
+
end
|
452
|
+
collector << " AS "
|
453
|
+
|
454
|
+
# sometimes these values are already quoted, if they are, don't double quote it
|
455
|
+
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
|
456
|
+
|
457
|
+
collector << '"' if quote
|
458
|
+
collector = visit o.right, collector
|
459
|
+
collector << '"' if quote
|
460
|
+
|
461
|
+
collector
|
462
|
+
end
|
463
|
+
|
390
464
|
# SQL Server does not know about REGEXP
|
391
465
|
def visit_Arel_Nodes_Regexp o, collector
|
392
466
|
collector = visit o.left, collector
|
@@ -554,7 +628,6 @@ module ArelExtensions
|
|
554
628
|
collector << ')'
|
555
629
|
collector
|
556
630
|
end
|
557
|
-
|
558
631
|
end
|
559
632
|
end
|
560
633
|
end
|
@@ -8,6 +8,7 @@ module ArelExtensions
|
|
8
8
|
|
9
9
|
DATE_FORMAT_DIRECTIVES = { # ISO C / POSIX
|
10
10
|
'%Y' => '%Y', '%C' => '', '%y' => '%y', '%m' => '%m', '%B' => '%M', '%b' => '%b', '%^b' => '%b', # year, month
|
11
|
+
'%V' => '%v', '%G' => '%x', # ISO week number and year of week
|
11
12
|
'%d' => '%d', '%e' => '%e', '%j' => '%j', '%w' => '%w', '%A' => '%W', # day, weekday
|
12
13
|
'%H' => '%H', '%k' => '%k', '%I' => '%I', '%l' => '%l', '%P' => '%p', '%p' => '%p', # hours
|
13
14
|
'%M' => '%i', '%S' => '%S', '%L' => '', '%N' => '%f', '%z' => ''
|
@@ -209,7 +210,21 @@ module ArelExtensions
|
|
209
210
|
when :date, :datetime, :time
|
210
211
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
211
212
|
collector << "DATE_FORMAT("
|
213
|
+
collector << "CONVERT_TZ(" if o.time_zone
|
212
214
|
collector = visit o.left, collector
|
215
|
+
case o.time_zone
|
216
|
+
when Hash
|
217
|
+
src_tz, dst_tz = o.time_zone.first
|
218
|
+
collector << COMMA
|
219
|
+
collector = visit Arel::Nodes.build_quoted(src_tz), collector
|
220
|
+
collector << COMMA
|
221
|
+
collector = visit Arel::Nodes.build_quoted(dst_tz), collector
|
222
|
+
collector << ')'
|
223
|
+
when String
|
224
|
+
collector << COMMA << "'UTC'" << COMMA
|
225
|
+
collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
|
226
|
+
collector << ')'
|
227
|
+
end
|
213
228
|
collector << COMMA
|
214
229
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
215
230
|
collector << ")"
|
@@ -6,6 +6,7 @@ module ArelExtensions
|
|
6
6
|
DATE_MAPPING = {'d' => 'DAY', 'm' => 'MONTH', 'w' => 'IW', 'y' => 'YEAR', 'wd' => 'D', 'h' => 'HOUR', 'mn' => 'MINUTE', 's' => 'SECOND'}
|
7
7
|
DATE_FORMAT_DIRECTIVES = {
|
8
8
|
'%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY', '%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
|
9
|
+
'%V' => 'IW', '%G' => 'IYYY', # ISO week number and year of week
|
9
10
|
'%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
|
10
11
|
'%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
|
11
12
|
'%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz' # seconds, subseconds
|
@@ -447,7 +448,19 @@ module ArelExtensions
|
|
447
448
|
def visit_ArelExtensions_Nodes_Format o, collector
|
448
449
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
449
450
|
collector << "TO_CHAR("
|
451
|
+
collector << "CAST(" if o.time_zone
|
450
452
|
collector = visit o.left, collector
|
453
|
+
case o.time_zone
|
454
|
+
when Hash
|
455
|
+
src_tz, dst_tz = o.time_zone.first
|
456
|
+
collector << ' as timestamp) at time zone '
|
457
|
+
collector = visit Arel::Nodes.build_quoted(src_tz), collector
|
458
|
+
collecto < ' at time zone '
|
459
|
+
collector = visit Arel::Nodes.build_quoted(dst_tz), collector
|
460
|
+
when String
|
461
|
+
collector << ' as timestamp) at time zone '
|
462
|
+
collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
|
463
|
+
end
|
451
464
|
collector << COMMA
|
452
465
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
453
466
|
collector << ")"
|
@@ -517,7 +530,7 @@ module ArelExtensions
|
|
517
530
|
if element.is_a?(Time)
|
518
531
|
ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
|
519
532
|
elsif element.is_a?(Arel::Attributes::Attribute)
|
520
|
-
col =
|
533
|
+
col = ArelExtensions::column_of(element.relation.table_name, element.name.to_s)
|
521
534
|
if col && (col.type == :time)
|
522
535
|
ArelExtensions::Nodes::Format.new [element, '%H:%M:%S']
|
523
536
|
else
|
@@ -9,9 +9,10 @@ module ArelExtensions
|
|
9
9
|
DATE_FORMAT_DIRECTIVES = {
|
10
10
|
'%Y' => 'YYYY', '%C' => 'CC', '%y' => 'YY',
|
11
11
|
'%m' => 'MM', '%B' => 'Month', '%^B' => 'MONTH', '%b' => 'Mon', '%^b' => 'MON',
|
12
|
-
'%
|
12
|
+
'%V' => 'IW', '%G' => 'IYYY', # ISO week number and year of week
|
13
|
+
'%d' => 'DD', '%e' => 'FMDD', '%j' => 'DDD', '%w' => '', '%A' => 'Day', # day, weekday
|
13
14
|
'%H' => 'HH24', '%k' => '', '%I' => 'HH', '%l' => '', '%P' => 'am', '%p' => 'AM', # hours
|
14
|
-
'%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz',
|
15
|
+
'%M' => 'MI', '%S' => 'SS', '%L' => 'MS', '%N' => 'US', '%z' => 'tz', # seconds, subseconds
|
15
16
|
'%%' => '%',
|
16
17
|
}.freeze
|
17
18
|
|
@@ -90,11 +91,11 @@ module ArelExtensions
|
|
90
91
|
|
91
92
|
# sometimes these values are already quoted, if they are, don't double quote it
|
92
93
|
quote = o.right.is_a?(Arel::Nodes::SqlLiteral) && o.right[0] != '"' && o.right[-1] != '"'
|
93
|
-
|
94
|
+
|
94
95
|
collector << '"' if quote
|
95
96
|
collector = visit o.right, collector
|
96
97
|
collector << '"' if quote
|
97
|
-
|
98
|
+
|
98
99
|
collector
|
99
100
|
end
|
100
101
|
|
@@ -175,7 +176,19 @@ module ArelExtensions
|
|
175
176
|
def visit_ArelExtensions_Nodes_Format o, collector
|
176
177
|
fmt = ArelExtensions::Visitors::strftime_to_format(o.iso_format, DATE_FORMAT_DIRECTIVES)
|
177
178
|
collector << "TO_CHAR("
|
179
|
+
collector << '(' if o.time_zone
|
178
180
|
collector = visit o.left, collector
|
181
|
+
case o.time_zone
|
182
|
+
when Hash
|
183
|
+
src_tz, dst_tz = o.time_zone.first
|
184
|
+
collector << ') AT TIME ZONE '
|
185
|
+
collector = visit Arel::Nodes.build_quoted(src_tz), collector
|
186
|
+
collector << ' AT TIME ZONE '
|
187
|
+
collector = visit Arel::Nodes.build_quoted(dst_tz), collector
|
188
|
+
when String
|
189
|
+
collector << ') AT TIME ZONE '
|
190
|
+
collector = visit Arel::Nodes.build_quoted(o.time_zone), collector
|
191
|
+
end
|
179
192
|
collector << COMMA
|
180
193
|
collector = visit Arel::Nodes.build_quoted(fmt), collector
|
181
194
|
collector << ")"
|
@@ -289,9 +302,9 @@ module ArelExtensions
|
|
289
302
|
return collector
|
290
303
|
end
|
291
304
|
end
|
292
|
-
collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM "
|
305
|
+
collector << "EXTRACT(#{DATE_MAPPING[o.left]} FROM CAST("
|
293
306
|
collector = visit o.right, collector
|
294
|
-
collector << ")"
|
307
|
+
collector << " AS TIMESTAMP WITH TIME ZONE))"
|
295
308
|
collector << " * (INTERVAL '1' #{interval})" if interval && o.with_interval
|
296
309
|
collector
|
297
310
|
end
|
@@ -374,7 +387,7 @@ module ArelExtensions
|
|
374
387
|
when :number, :decimal, :float
|
375
388
|
Arel::Nodes::SqlLiteral.new('numeric')
|
376
389
|
when :datetime
|
377
|
-
Arel::Nodes::SqlLiteral.new('timestamp')
|
390
|
+
Arel::Nodes::SqlLiteral.new('timestamp with time zone')
|
378
391
|
when :date
|
379
392
|
Arel::Nodes::SqlLiteral.new('date')
|
380
393
|
when :binary
|