activerecord-jdbcteradata-adapter 0.3.2 → 0.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.
- data/Gemfile.lock +1 -1
- data/activerecord-jdbcteradata-adapter.gemspec +1 -1
- data/lib/activerecord-jdbcteradata-adapter.rb +1 -1
- data/lib/arel/visitors/teradata.rb +357 -70
- data/lib/arjdbc/teradata/adapter.rb +28 -21
- data/lib/arjdbc/teradata/connection_methods.rb +1 -1
- data/spec/adapter_spec.rb +2 -2
- data/spec/associations_spec.rb +1 -1
- data/spec/models/active_record_schema.rb +4 -4
- metadata +2 -2
data/Gemfile.lock
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "activerecord-jdbcteradata-adapter"
|
3
|
-
s.version = "0.3.
|
3
|
+
s.version = "0.3.3"
|
4
4
|
s.authors = ["Chris Parker"]
|
5
5
|
s.email = [ "mrcsparker@gmail.com"]
|
6
6
|
s.homepage = "https://github.com/mrcsparker/activerecord-jdbcteradata-adapter"
|
@@ -1,104 +1,391 @@
|
|
1
|
+
# A good deal of this code is from https://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
2
|
+
|
3
|
+
require 'arel'
|
4
|
+
|
1
5
|
module Arel
|
6
|
+
|
7
|
+
module Nodes
|
8
|
+
|
9
|
+
# Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
|
10
|
+
# collection of them. See SelectManager#order for more details.
|
11
|
+
class Ordering < Arel::Nodes::Unary
|
12
|
+
def hash
|
13
|
+
expr.hash
|
14
|
+
end
|
15
|
+
def ==(other)
|
16
|
+
other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr
|
17
|
+
end
|
18
|
+
def eql?(other)
|
19
|
+
self == other
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class SelectManager < Arel::TreeManager
|
26
|
+
|
27
|
+
AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::TeradataAdapter'.freeze
|
28
|
+
|
29
|
+
# Getting real Ordering objects is very important for us. We need to be able to call #uniq on
|
30
|
+
# a colleciton of them reliably as well as using their true object attributes to mutate them
|
31
|
+
# to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
|
32
|
+
# is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects.
|
33
|
+
alias :order_without_sqlserver :order
|
34
|
+
def order(*expr)
|
35
|
+
return order_without_sqlserver(*expr) unless engine_activerecord_sqlserver_adapter?
|
36
|
+
@ast.orders.concat(expr.map{ |x|
|
37
|
+
case x
|
38
|
+
when Arel::Attributes::Attribute
|
39
|
+
table = Arel::Table.new(x.relation.table_alias || x.relation.name)
|
40
|
+
e = table[x.name]
|
41
|
+
Arel::Nodes::Ascending.new e
|
42
|
+
when Arel::Nodes::Ordering
|
43
|
+
x
|
44
|
+
when String
|
45
|
+
x.split(',').map do |s|
|
46
|
+
s = x if x.strip =~ /\A\b\w+\b\(.*,.*\)(\s+(ASC|DESC))?\Z/i # Allow functions with comma(s) to pass thru.
|
47
|
+
s.strip!
|
48
|
+
d = s =~ /(ASC|DESC)\Z/i ? $1.upcase : nil
|
49
|
+
e = d.nil? ? s : s.mb_chars[0...-d.length].strip
|
50
|
+
e = Arel.sql(e)
|
51
|
+
d && d == "DESC" ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
e = Arel.sql(x.to_s)
|
55
|
+
Arel::Nodes::Ascending.new e
|
56
|
+
end
|
57
|
+
}.flatten)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# A friendly over ride that allows us to put a special lock object that can have a default or pass
|
62
|
+
# custom string hints down. See the visit_Arel_Nodes_LockWithTeradata delegation method.
|
63
|
+
alias :lock_without_sqlserver :lock
|
64
|
+
def lock(locking=true)
|
65
|
+
if engine_activerecord_sqlserver_adapter?
|
66
|
+
case locking
|
67
|
+
when true
|
68
|
+
locking = Arel.sql('WITH(HOLDLOCK, ROWLOCK)')
|
69
|
+
when Arel::Nodes::SqlLiteral
|
70
|
+
when String
|
71
|
+
locking = Arel.sql locking
|
72
|
+
end
|
73
|
+
@ast.lock = Arel::Nodes::Lock.new(locking)
|
74
|
+
self
|
75
|
+
else
|
76
|
+
lock_without_sqlserver(locking)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def engine_activerecord_sqlserver_adapter?
|
83
|
+
@engine.connection && @engine.connection.class.name == AR_CA_SQLSA_NAME
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
2
88
|
module Visitors
|
3
89
|
class Teradata < Arel::Visitors::ToSql
|
4
90
|
|
5
|
-
|
6
|
-
|
7
|
-
|
91
|
+
private
|
92
|
+
|
93
|
+
# Teradata ToSql/Visitor (Overides)
|
94
|
+
|
95
|
+
def visit_Arel_Nodes_SelectStatement(o)
|
96
|
+
if complex_count_sql?(o)
|
97
|
+
visit_Arel_Nodes_SelectStatementForComplexCount(o)
|
98
|
+
elsif o.offset
|
99
|
+
visit_Arel_Nodes_SelectStatementWithOffset(o)
|
100
|
+
else
|
101
|
+
visit_Arel_Nodes_SelectStatementWithOutOffset(o)
|
8
102
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
103
|
+
end
|
104
|
+
|
105
|
+
def visit_Arel_Nodes_UpdateStatement(o)
|
106
|
+
if o.orders.any? && o.limit.nil?
|
107
|
+
o.limit = Nodes::Limit.new(9223372036854775807)
|
108
|
+
end
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
def visit_Arel_Nodes_Offset(o)
|
113
|
+
"WHERE [__rnt].[__rn] > (#{visit o.expr})"
|
114
|
+
end
|
115
|
+
|
116
|
+
def visit_Arel_Nodes_Limit(o)
|
117
|
+
"TOP #{visit o.expr}"
|
118
|
+
end
|
21
119
|
|
22
|
-
|
23
|
-
|
24
|
-
|
120
|
+
def visit_Arel_Nodes_Lock(o)
|
121
|
+
visit o.expr
|
122
|
+
end
|
123
|
+
|
124
|
+
def visit_Arel_Nodes_Ordering(o)
|
125
|
+
if o.respond_to?(:direction)
|
126
|
+
"#{visit o.expr} #{o.ascending? ? 'ASC' : 'DESC'}"
|
25
127
|
else
|
26
|
-
|
128
|
+
visit o.expr
|
27
129
|
end
|
28
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
def visit_Arel_Nodes_Bin(o)
|
133
|
+
"#{visit o.expr} #{@connection.cs_equality_operator}"
|
29
134
|
end
|
30
135
|
|
31
|
-
#
|
32
|
-
# Lots of this code was pulled from the activerecord JDBC MSSQL driver
|
136
|
+
# Teradata ToSql/Visitor (Additions)
|
33
137
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
138
|
+
def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false)
|
139
|
+
find_and_fix_uncorrelated_joins_in_select_statement(o)
|
140
|
+
core = o.cores.first
|
141
|
+
projections = core.projections
|
142
|
+
groups = core.groups
|
143
|
+
orders = o.orders.uniq
|
144
|
+
if windowed
|
145
|
+
projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x) }
|
146
|
+
groups = projections.map { |x| projection_without_expression(x) } if windowed_single_distinct_select_statement?(o) && groups.empty?
|
147
|
+
groups += orders.map { |x| Arel.sql(x.expr) } if windowed_single_distinct_select_statement?(o)
|
148
|
+
elsif eager_limiting_select_statement?(o)
|
149
|
+
projections = projections.map { |x| projection_without_expression(x) }
|
150
|
+
groups = projections.map { |x| projection_without_expression(x) }
|
151
|
+
orders = orders.map do |x|
|
152
|
+
expr = Arel.sql projection_without_expression(x.expr)
|
153
|
+
x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr])
|
154
|
+
end
|
155
|
+
elsif top_one_everything_for_through_join?(o)
|
156
|
+
projections = projections.map { |x| projection_without_expression(x) }
|
157
|
+
end
|
158
|
+
[ ("SELECT" if !windowed),
|
159
|
+
(visit(core.set_quantifier) if core.set_quantifier && !windowed),
|
160
|
+
(visit(o.limit) if o.limit && !windowed),
|
161
|
+
(projections.map{ |x| v = visit(x); v == "1" ? "1 AS [__wrp]" : v }.join(', ')),
|
162
|
+
(source_with_lock_for_select_statement(o)),
|
163
|
+
("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?),
|
164
|
+
("GROUP BY #{groups.map { |x| visit x }.join ', ' }" unless groups.empty?),
|
165
|
+
(visit(core.having) if core.having),
|
166
|
+
("ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}" if !orders.empty? && !windowed)
|
167
|
+
].compact.join ' '
|
168
|
+
end
|
169
|
+
|
170
|
+
def visit_Arel_Nodes_SelectStatementWithOffset(o)
|
171
|
+
core = o.cores.first
|
172
|
+
o.limit ||= Arel::Nodes::Limit.new(9223372036854775807)
|
173
|
+
orders = rowtable_orders(o)
|
174
|
+
[ "SELECT",
|
175
|
+
(visit(o.limit) if o.limit && !windowed_single_distinct_select_statement?(o)),
|
176
|
+
(rowtable_projections(o).map{ |x| visit(x) }.join(', ')),
|
177
|
+
"FROM (",
|
178
|
+
"SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],",
|
179
|
+
visit_Arel_Nodes_SelectStatementWithOutOffset(o,true),
|
180
|
+
") AS [__rnt]",
|
181
|
+
(visit(o.offset) if o.offset),
|
182
|
+
"ORDER BY [__rnt].[__rn] ASC"
|
183
|
+
].compact.join ' '
|
184
|
+
end
|
185
|
+
|
186
|
+
def visit_Arel_Nodes_SelectStatementForComplexCount(o)
|
187
|
+
core = o.cores.first
|
188
|
+
o.limit.expr = Arel.sql("#{o.limit.expr} + #{o.offset ? o.offset.expr : 0}") if o.limit
|
189
|
+
orders = rowtable_orders(o)
|
190
|
+
[ "SELECT COUNT([count]) AS [count_id]",
|
191
|
+
"FROM (",
|
192
|
+
"SELECT",
|
193
|
+
(visit(o.limit) if o.limit),
|
194
|
+
"ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],",
|
195
|
+
"1 AS [count]",
|
196
|
+
(source_with_lock_for_select_statement(o)),
|
197
|
+
("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?),
|
198
|
+
("GROUP BY #{core.groups.map { |x| visit x }.join ', ' }" unless core.groups.empty?),
|
199
|
+
(visit(core.having) if core.having),
|
200
|
+
("ORDER BY #{o.orders.map{ |x| visit(x) }.join(', ')}" if !o.orders.empty?),
|
201
|
+
") AS [__rnt]",
|
202
|
+
(visit(o.offset) if o.offset)
|
203
|
+
].compact.join ' '
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# Teradata Helpers
|
208
|
+
|
209
|
+
def source_with_lock_for_select_statement(o)
|
210
|
+
core = o.cores.first
|
211
|
+
source = "FROM #{visit(core.source).strip}" if core.source
|
212
|
+
if source && o.lock
|
213
|
+
lock = visit o.lock
|
214
|
+
index = source.match(/FROM [\w\[\]\.]+/)[0].mb_chars.length
|
215
|
+
source.insert index, " #{lock}"
|
39
216
|
else
|
40
|
-
|
217
|
+
source
|
41
218
|
end
|
42
219
|
end
|
43
220
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
221
|
+
def table_from_select_statement(o)
|
222
|
+
core = o.cores.first
|
223
|
+
# TODO: [ARel 2.2] Use #from/#source vs. #froms
|
224
|
+
# if Arel::Table === core.from
|
225
|
+
# core.from
|
226
|
+
# elsif Arel::Nodes::SqlLiteral === core.from
|
227
|
+
# Arel::Table.new(core.from, @engine)
|
228
|
+
# elsif Arel::Nodes::JoinSource === core.source
|
229
|
+
# Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
|
230
|
+
# end
|
231
|
+
table_finder = lambda { |x|
|
232
|
+
case x
|
233
|
+
when Arel::Table
|
234
|
+
x
|
235
|
+
when Arel::Nodes::SqlLiteral
|
236
|
+
Arel::Table.new(x, @engine)
|
237
|
+
when Arel::Nodes::Join
|
238
|
+
table_finder.call(x.left)
|
239
|
+
end
|
240
|
+
}
|
241
|
+
table_finder.call(core.froms)
|
48
242
|
end
|
49
243
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
244
|
+
def single_distinct_select_statement?(o)
|
245
|
+
projections = o.cores.first.projections
|
246
|
+
p1 = projections.first
|
247
|
+
projections.size == 1 &&
|
248
|
+
((p1.respond_to?(:distinct) && p1.distinct) ||
|
249
|
+
p1.respond_to?(:include?) && p1.include?('DISTINCT'))
|
250
|
+
end
|
251
|
+
|
252
|
+
def windowed_single_distinct_select_statement?(o)
|
253
|
+
o.limit && o.offset && single_distinct_select_statement?(o)
|
254
|
+
end
|
255
|
+
|
256
|
+
def single_distinct_select_everything_statement?(o)
|
257
|
+
single_distinct_select_statement?(o) && visit(o.cores.first.projections.first).ends_with?(".*")
|
258
|
+
end
|
259
|
+
|
260
|
+
def top_one_everything_for_through_join?(o)
|
261
|
+
single_distinct_select_everything_statement?(o) &&
|
262
|
+
(o.limit && !o.offset) &&
|
263
|
+
join_in_select_statement?(o)
|
58
264
|
end
|
59
265
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
replace_limit_offset!(sql, options[:limit], options[:offset], order)
|
266
|
+
def all_projections_aliased_in_select_statement?(o)
|
267
|
+
projections = o.cores.first.projections
|
268
|
+
projections.all? do |x|
|
269
|
+
visit(x).split(',').all? { |y| y.include?(' AS ') }
|
65
270
|
end
|
66
271
|
end
|
67
272
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
273
|
+
def function_select_statement?(o)
|
274
|
+
core = o.cores.first
|
275
|
+
core.projections.any? { |x| Arel::Nodes::Function === x }
|
276
|
+
end
|
277
|
+
|
278
|
+
def eager_limiting_select_statement?(o)
|
279
|
+
core = o.cores.first
|
280
|
+
single_distinct_select_statement?(o) &&
|
281
|
+
(o.limit && !o.offset) &&
|
282
|
+
core.groups.empty? &&
|
283
|
+
!single_distinct_select_everything_statement?(o)
|
284
|
+
end
|
285
|
+
|
286
|
+
def join_in_select_statement?(o)
|
287
|
+
core = o.cores.first
|
288
|
+
core.source.right.any? { |x| Arel::Nodes::Join === x }
|
289
|
+
end
|
290
|
+
|
291
|
+
def complex_count_sql?(o)
|
292
|
+
core = o.cores.first
|
293
|
+
core.projections.size == 1 &&
|
294
|
+
Arel::Nodes::Count === core.projections.first &&
|
295
|
+
o.limit &&
|
296
|
+
!join_in_select_statement?(o)
|
297
|
+
end
|
298
|
+
|
299
|
+
def select_primary_key_sql?(o)
|
300
|
+
core = o.cores.first
|
301
|
+
return false if core.projections.size != 1
|
302
|
+
p = core.projections.first
|
303
|
+
t = table_from_select_statement(o)
|
304
|
+
Arel::Attributes::Attribute === p && t.primary_key && t.primary_key.name == p.name
|
305
|
+
end
|
306
|
+
|
307
|
+
def find_and_fix_uncorrelated_joins_in_select_statement(o)
|
308
|
+
core = o.cores.first
|
309
|
+
# TODO: [ARel 2.2] Use #from/#source vs. #froms
|
310
|
+
# return if !join_in_select_statement?(o) || core.source.right.size != 2
|
311
|
+
# j1 = core.source.right.first
|
312
|
+
# j2 = core.source.right.second
|
313
|
+
# return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::StringJoin === j2
|
314
|
+
# j1_tn = j1.left.name
|
315
|
+
# j2_tn = j2.left.match(/JOIN \[(.*)\].*ON/).try(:[],1)
|
316
|
+
# return unless j1_tn == j2_tn
|
317
|
+
# crltd_tn = "#{j1_tn}_crltd"
|
318
|
+
# j1.left.table_alias = crltd_tn
|
319
|
+
# j1.right.expr.left.relation.table_alias = crltd_tn
|
320
|
+
return if !join_in_select_statement?(o) || !(Arel::Nodes::StringJoin === core.froms)
|
321
|
+
j1 = core.froms.left
|
322
|
+
j2 = core.froms.right
|
323
|
+
return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::SqlLiteral === j2 && j2.include?('JOIN ')
|
324
|
+
j1_tn = j1.right.name
|
325
|
+
j2_tn = j2.match(/JOIN \[(.*)\].*ON/).try(:[],1)
|
326
|
+
return unless j1_tn == j2_tn
|
327
|
+
on_index = j2.index(' ON ')
|
328
|
+
j2.insert on_index, " AS [#{j2_tn}_crltd]"
|
329
|
+
j2.sub! "[#{j2_tn}].", "[#{j2_tn}_crltd]."
|
330
|
+
end
|
331
|
+
|
332
|
+
def rowtable_projections(o)
|
333
|
+
core = o.cores.first
|
334
|
+
if windowed_single_distinct_select_statement?(o) && core.groups.blank?
|
335
|
+
tn = table_from_select_statement(o).name
|
336
|
+
core.projections.map do |x|
|
337
|
+
x.dup.tap do |p|
|
338
|
+
p.sub! 'DISTINCT', ''
|
339
|
+
p.insert 0, visit(o.limit) if o.limit
|
340
|
+
p.gsub! /\[?#{tn}\]?\./, '[__rnt].'
|
341
|
+
p.strip!
|
342
|
+
end
|
78
343
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
344
|
+
elsif single_distinct_select_statement?(o)
|
345
|
+
tn = table_from_select_statement(o).name
|
346
|
+
core.projections.map do |x|
|
347
|
+
x.dup.tap do |p|
|
348
|
+
p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit)}".strip if o.limit
|
349
|
+
p.gsub! /\[?#{tn}\]?\./, '[__rnt].'
|
350
|
+
p.strip!
|
351
|
+
end
|
82
352
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
353
|
+
elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o)
|
354
|
+
core.projections.map do |x|
|
355
|
+
Arel.sql visit(x).split(',').map{ |y| y.split(' AS ').last.strip }.join(', ')
|
356
|
+
end
|
357
|
+
elsif select_primary_key_sql?(o)
|
358
|
+
[Arel.sql("[__rnt].#{quote_column_name(core.projections.first.name)}")]
|
359
|
+
else
|
360
|
+
[Arel.sql('[__rnt].*')]
|
86
361
|
end
|
87
|
-
sql
|
88
362
|
end
|
89
363
|
|
90
|
-
def
|
91
|
-
|
364
|
+
def rowtable_orders(o)
|
365
|
+
core = o.cores.first
|
366
|
+
if !o.orders.empty?
|
367
|
+
o.orders
|
368
|
+
else
|
369
|
+
t = table_from_select_statement(o)
|
370
|
+
c = t.primary_key || t.columns.first
|
371
|
+
[c.asc]
|
372
|
+
end.uniq
|
92
373
|
end
|
93
374
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
375
|
+
# TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral.
|
376
|
+
def projection_without_expression(projection)
|
377
|
+
Arel.sql(visit(projection).split(',').map do |x|
|
378
|
+
x.strip!
|
379
|
+
x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/,'\3')
|
380
|
+
x.sub!(/^DISTINCT\s*/,'')
|
381
|
+
x.sub!(/TOP\s*\(\d+\)\s*/i,'')
|
382
|
+
x.strip
|
383
|
+
end.join(', '))
|
98
384
|
end
|
99
385
|
|
100
|
-
# </Helpers>
|
101
|
-
|
102
386
|
end
|
103
387
|
end
|
388
|
+
|
104
389
|
end
|
390
|
+
|
391
|
+
Arel::Visitors::VISITORS['teradata'] = Arel::Visitors::Teradata
|
@@ -59,16 +59,16 @@ module ::ArJdbc
|
|
59
59
|
super.merge({
|
60
60
|
:primary_key => 'INTEGER PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1 MINVALUE -2147483647 MAXVALUE 1000000000 NO CYCLE)',
|
61
61
|
:string => { :name => 'VARCHAR', :limit => 255 },
|
62
|
-
:integer => { :name =>
|
63
|
-
:float => { :name =>
|
64
|
-
:decimal => { :name =>
|
65
|
-
:datetime => { :name =>
|
66
|
-
:timestamp => { :name =>
|
67
|
-
:time => { :name =>
|
68
|
-
:date => { :name =>
|
69
|
-
:binary => { :name =>
|
70
|
-
:boolean => { :name =>
|
71
|
-
:raw => { :name =>
|
62
|
+
:integer => { :name => 'INTEGER'},
|
63
|
+
:float => { :name => 'FLOAT'},
|
64
|
+
:decimal => { :name => 'DECIMAL'},
|
65
|
+
:datetime => { :name => 'TIMESTAMP'},
|
66
|
+
:timestamp => { :name => 'TIMESTAMP'},
|
67
|
+
:time => { :name => 'TIMESTAMP'},
|
68
|
+
:date => { :name => 'DATE'},
|
69
|
+
:binary => { :name => 'BLOB'},
|
70
|
+
:boolean => { :name => 'BYTEINT'},
|
71
|
+
:raw => { :name => 'BYTE'}
|
72
72
|
})
|
73
73
|
end
|
74
74
|
|
@@ -105,7 +105,7 @@ module ::ArJdbc
|
|
105
105
|
private :_execute
|
106
106
|
|
107
107
|
def _table_name_from_insert(sql)
|
108
|
-
sql.split(
|
108
|
+
sql.split(' ', 4)[2].gsub('"', '').gsub("'", '')
|
109
109
|
end
|
110
110
|
private :_table_name_from_insert
|
111
111
|
|
@@ -125,6 +125,9 @@ module ::ArJdbc
|
|
125
125
|
#- insert_sql
|
126
126
|
|
127
127
|
#- tables
|
128
|
+
def tables
|
129
|
+
@connection.tables(nil, database_name, nil, %w(TABLE))
|
130
|
+
end
|
128
131
|
|
129
132
|
#- table_exists?
|
130
133
|
def table_exists?(table_name)
|
@@ -137,9 +140,9 @@ module ::ArJdbc
|
|
137
140
|
# TODO: Multiple indexes per column
|
138
141
|
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition # :nodoc:
|
139
142
|
def indexes(table_name, name = nil, schema_name = nil)
|
140
|
-
result = select_rows(
|
141
|
-
|
142
|
-
|
143
|
+
result = select_rows('SELECT' <<
|
144
|
+
' DatabaseName, TableName, ColumnName, IndexType, IndexName, UniqueFlag' <<
|
145
|
+
' FROM DBC.Indices' <<
|
143
146
|
" WHERE TableName = '#{table_name}' AND DatabaseName = '#{database_name}'")
|
144
147
|
|
145
148
|
result.map do |row|
|
@@ -153,7 +156,7 @@ module ::ArJdbc
|
|
153
156
|
columns = []
|
154
157
|
columns << idx_column_name
|
155
158
|
|
156
|
-
IndexDefinition.new(idx_table_name, idx_index_name, (idx_unique_flag ==
|
159
|
+
IndexDefinition.new(idx_table_name, idx_index_name, (idx_unique_flag == 'Y'), columns)
|
157
160
|
end
|
158
161
|
end
|
159
162
|
|
@@ -202,9 +205,9 @@ module ::ArJdbc
|
|
202
205
|
|
203
206
|
#- remove_column
|
204
207
|
def remove_column(table_name, *column_names) #:nodoc:
|
205
|
-
|
208
|
+
column_names.flatten.each { |column_name|
|
206
209
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
207
|
-
|
210
|
+
}
|
208
211
|
end
|
209
212
|
|
210
213
|
#+ change_column
|
@@ -312,10 +315,14 @@ module ::ArJdbc
|
|
312
315
|
IDENTIFIER_LENGTH = 30 # :nodoc:
|
313
316
|
|
314
317
|
# maximum length of Teradata identifiers is 30
|
315
|
-
def table_alias_length; IDENTIFIER_LENGTH
|
316
|
-
|
317
|
-
def
|
318
|
-
|
318
|
+
def table_alias_length; IDENTIFIER_LENGTH
|
319
|
+
end # :nodoc:
|
320
|
+
def table_name_length; IDENTIFIER_LENGTH
|
321
|
+
end # :nodoc:
|
322
|
+
def index_name_length; IDENTIFIER_LENGTH
|
323
|
+
end # :nodoc:
|
324
|
+
def column_name_length; IDENTIFIER_LENGTH
|
325
|
+
end # :nodoc:
|
319
326
|
|
320
327
|
end
|
321
328
|
end
|
@@ -11,7 +11,7 @@ class ActiveRecord::Base
|
|
11
11
|
config[:host] ||= 'localhost'
|
12
12
|
config[:port] ||= 1025
|
13
13
|
config[:url] ||= "jdbc:teradata://#{config[:host]}/DATABASE=#{config[:database]},DBS_PORT=#{config[:port]},COP=OFF"
|
14
|
-
config[:driver] ||=
|
14
|
+
config[:driver] ||= 'com.teradata.jdbc.TeraDriver'
|
15
15
|
jdbc_connection(config)
|
16
16
|
end
|
17
17
|
alias_method :jdbcteradata_connection, :teradata_connection
|
data/spec/adapter_spec.rb
CHANGED
@@ -59,9 +59,9 @@ describe 'Adapter' do
|
|
59
59
|
it '#indexes' do
|
60
60
|
id_index = @adapter.indexes('articles').first
|
61
61
|
id_index.table.should eq('articles')
|
62
|
-
id_index.name.should ==
|
62
|
+
id_index.name.should == ''
|
63
63
|
id_index.unique.should be_true
|
64
|
-
id_index.columns.should eq(
|
64
|
+
id_index.columns.should eq(%w(id))
|
65
65
|
end
|
66
66
|
|
67
67
|
it '#pk_and_sequence_for' do
|
data/spec/associations_spec.rb
CHANGED
@@ -88,7 +88,7 @@ class CreateActiveRecordSchema < ActiveRecord::Migration
|
|
88
88
|
t.string :color
|
89
89
|
end
|
90
90
|
|
91
|
-
create_table
|
91
|
+
create_table 'CamelCase', :force => true do |t|
|
92
92
|
t.string :name
|
93
93
|
end
|
94
94
|
|
@@ -154,10 +154,10 @@ class CreateActiveRecordSchema < ActiveRecord::Migration
|
|
154
154
|
t.integer :client_of
|
155
155
|
t.integer :rating, :default => 1
|
156
156
|
t.integer :account_id
|
157
|
-
t.string :description, :default =>
|
157
|
+
t.string :description, :default => ''
|
158
158
|
end
|
159
159
|
|
160
|
-
add_index :ar_companies, [:firm_id, :type, :rating, :ruby_type], :name =>
|
160
|
+
add_index :ar_companies, [:firm_id, :type, :rating, :ruby_type], :name => 'company_index'
|
161
161
|
|
162
162
|
create_table :ar_vegetables, :force => true do |t|
|
163
163
|
t.string :name
|
@@ -275,7 +275,7 @@ class CreateActiveRecordSchema < ActiveRecord::Migration
|
|
275
275
|
end
|
276
276
|
|
277
277
|
create_table :ar_integer_limits, :force => true do |t|
|
278
|
-
t.integer :
|
278
|
+
t.integer :'c_int_without_limit'
|
279
279
|
(1..8).each do |i|
|
280
280
|
t.integer :"c_int_#{i}", :limit => i
|
281
281
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-jdbcteradata-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|