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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-jdbcteradata-adapter (0.3.1)
4
+ activerecord-jdbcteradata-adapter (0.3.2)
5
5
  activerecord
6
6
  activerecord-jdbc-adapter
7
7
  jdbc-teradata
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "activerecord-jdbcteradata-adapter"
3
- s.version = "0.3.2"
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,5 +1,5 @@
1
1
  module ArJdbc
2
2
  module Teradata
3
- VERSION = "0.2.0"
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -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
- def visit_Arel_Nodes_SelectStatement o
6
- if !o.limit && o.offset
7
- raise ActiveRecord::ActiveRecordError, "You must specify :limit with :offset."
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
- order = "ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?
10
- if o.limit
11
- if select_count?(o)
12
- subquery = true
13
- sql = o.cores.map do |x|
14
- x = x.dup
15
- x.projections = [Arel::Nodes::SqlLiteral.new("*")]
16
- visit_Arel_Nodes_SelectCore x
17
- end.join
18
- else
19
- sql = o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join
20
- end
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
- order ||= "ORDER BY #{determine_order_clause(sql)}"
23
- replace_limit_offset!(sql, limit_for(o.limit).to_i, o.offset && o.offset.value.to_i, order)
24
- sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if subquery
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
- sql = super
128
+ visit o.expr
27
129
  end
28
- sql
130
+ end
131
+
132
+ def visit_Arel_Nodes_Bin(o)
133
+ "#{visit o.expr} #{@connection.cs_equality_operator}"
29
134
  end
30
135
 
31
- # <Helpers>
32
- # Lots of this code was pulled from the activerecord JDBC MSSQL driver
136
+ # Teradata ToSql/Visitor (Additions)
33
137
 
34
- def get_table_name(sql)
35
- if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
36
- $1
37
- elsif sql =~ /\bfrom\s+([^\(\s,]+)\s*/i
38
- $1
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
- nil
217
+ source
41
218
  end
42
219
  end
43
220
 
44
- def determine_order_clause(sql)
45
- return $1 if sql =~ /ORDER BY (.*)$/
46
- table_name = get_table_name(sql)
47
- "#{table_name}.#{determine_primary_key(table_name)}"
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 determine_primary_key(table_name)
51
- table_name = table_name.gsub('"', '')
52
- primary_key = @connection.columns(table_name).detect { |column| column.primary }
53
- return primary_key.name if primary_key
54
- # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
55
- @connection.columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
56
- # Give up and provide something which is going to crash almost certainly
57
- @connection.columns(table_name)[0].name
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 add_limit_offset!(sql, options)
61
- if options[:limit]
62
- order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
63
- sql.sub!(/ ORDER BY.*$/i, '')
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 replace_limit_offset!(sql, limit, offset, order)
69
- if limit
70
- offset ||= 0
71
- start_row = offset + 1
72
- end_row = offset + limit.to_i
73
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
74
- whole, select, rest_of_query = find_select.match(sql).to_a
75
- rest_of_query.strip!
76
- if rest_of_query[0...1] == "1" && rest_of_query !~ /1 AS/i
77
- rest_of_query[0] = "*"
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
- if rest_of_query[0] == "*"
80
- from_table = get_table_name(rest_of_query)
81
- rest_of_query = from_table + '.' + rest_of_query
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
- new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query}"
84
- new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
85
- sql.replace(new_sql)
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 limit_for(limit_or_node)
91
- limit_or_node.respond_to?(:expr) ? limit_or_node.expr.to_i : limit_or_node
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
- def select_count? o
95
- sel = o.cores.length == 1 && o.cores.first
96
- projections = sel && sel.projections.length == 1 && sel.projections
97
- projections && Arel::Nodes::Count === projections.first
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 => "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" }
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(" ", 4)[2].gsub('"', '').gsub("'", "")
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("SELECT" <<
141
- " DatabaseName, TableName, ColumnName, IndexType, IndexName, UniqueFlag" <<
142
- " FROM DBC.Indices" <<
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 == "Y"), columns)
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
- for column_name in column_names.flatten
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
- end
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; end # :nodoc:
316
- def table_name_length; IDENTIFIER_LENGTH; end # :nodoc:
317
- def index_name_length; IDENTIFIER_LENGTH; end # :nodoc:
318
- def column_name_length; IDENTIFIER_LENGTH; end # :nodoc:
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] ||= "com.teradata.jdbc.TeraDriver"
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([ 'id' ])
64
+ id_index.columns.should eq(%w(id))
65
65
  end
66
66
 
67
67
  it '#pk_and_sequence_for' do
@@ -19,7 +19,7 @@ describe 'AssociationsSpec' do
19
19
  purchase_order = PurchaseOrder.new
20
20
  purchase_order.product = vendor.products.first
21
21
 
22
- purchase_order.code = "ORDER1"
22
+ purchase_order.code = 'ORDER1'
23
23
  purchase_order.quantity = 2
24
24
  purchase_order.save
25
25
 
@@ -88,7 +88,7 @@ class CreateActiveRecordSchema < ActiveRecord::Migration
88
88
  t.string :color
89
89
  end
90
90
 
91
- create_table "CamelCase", :force => true do |t|
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 => "company_index"
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 :"c_int_without_limit"
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.2
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-05 00:00:00.000000000 Z
12
+ date: 2013-04-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake