activerecord-jdbcteradata-adapter 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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