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.
@@ -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
- begin
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
- begin
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
- begin
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
- begin
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.to_i
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
- Arel.sql('second')
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(Arel::Visitors::MSSQL::DATE_MAPPING[v.left[0..-2]])
163
+ Arel.sql(ArelExtensions::Visitors::MSSQL::LOADED_VISITOR::DATE_MAPPING[v.left[0..-2]])
145
164
  else
146
- Arel.sql(Arel::Visitors::MSSQL::DATE_MAPPING[v.left])
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.first
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
- begin
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
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "2.1.0".freeze
2
+ VERSION = "2.1.1".freeze
3
3
  end
@@ -2,27 +2,37 @@ module ArelExtensions
2
2
  module Visitors
3
3
  module MSSQL
4
4
 
5
- Arel::Visitors::MSSQL::DATE_MAPPING = {
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
- Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES = {
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
- Arel::Visitors::MSSQL::DATE_FORMAT_REGEX =
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
- Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES
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
- Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS = {
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 << Arel::Visitors::MSSQL::COMMA if i != 0
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
- when :ruby_time, :datetime, :time then 'DATEDIFF(second'
106
- else 'DATEDIFF(day'
107
- end
108
- collector << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::COMMA
127
+ collector << LOADED_VISITOR::COMMA
118
128
  collector << "-("
119
129
  collector = visit da.mssql_value(o.right), collector
120
130
  collector << ")"
121
- collector << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::COMMA
142
+ collector << LOADED_VISITOR::COMMA
133
143
  collector = visit o.mssql_value(o.right), collector
134
- collector << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::DATE_MAPPING[left]
148
- collector << Arel::Visitors::MSSQL::COMMA
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
- collector << "#{o.bytewise ? 'DATALENGTH' : 'LEN'}("
159
- collector = visit o.expr, collector
160
- collector << ")"
161
- collector
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 << Arel::Visitors::MSSQL::COMMA if i != 0
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 << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::COMMA
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 << Arel::Visitors::MSSQL::COMMA
209
+ collector << LOADED_VISITOR::COMMA
191
210
  collector = visit o.expressions[1], collector
192
- collector << Arel::Visitors::MSSQL::COMMA
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
- if o.right
200
- collector << "REPLACE(REPLACE(LTRIM(RTRIM(REPLACE(REPLACE("
201
- collector = visit o.left, collector
202
- collector << ", ' ', '~'), "
203
- collector = visit o.right, collector
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, Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES)
259
- if fmt = Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f]
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 << Arel::Visitors::MSSQL::COMMA
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
- collector << Arel::Visitors::MSSQL::COMMA
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(Arel::Visitors::MSSQL::DATE_FORMAT_REGEX)
276
- dir = Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES[s.matched]
277
- collector << 'LTRIM(STR(DATEPART('
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 << Arel::Visitors::MSSQL::COMMA
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
- collector << ')))'
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 << Arel::Visitors::MSSQL::COMMA if i != 0
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 << Arel::Visitors::MSSQL::COMMA if i != 0
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 = Arel::Table.engine.connection.schema_cache.columns_hash(element.relation.table_name)[element.name.to_s]
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 = Arel::Table.engine.connection.schema_cache.columns_hash(element.relation.table_name)[element.name.to_s]
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
- collector << " AS \""
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
 
@@ -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