activerecord-jdbcteradata-adapter 0.3.0 → 0.3.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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-jdbcteradata-adapter (0.2.0)
4
+ activerecord-jdbcteradata-adapter (0.3.1)
5
5
  activerecord
6
6
  activerecord-jdbc-adapter
7
7
  jdbc-teradata
@@ -23,11 +23,11 @@ GEM
23
23
  multi_json (~> 1.0)
24
24
  arel (3.0.2)
25
25
  builder (3.0.4)
26
- diff-lcs (1.2.1)
26
+ diff-lcs (1.2.2)
27
27
  i18n (0.6.1)
28
28
  jdbc-teradata (0.2.0)
29
29
  multi_json (1.7.2)
30
- rake (10.0.3)
30
+ rake (10.0.4)
31
31
  rspec (2.13.0)
32
32
  rspec-core (~> 2.13.0)
33
33
  rspec-expectations (~> 2.13.0)
@@ -40,6 +40,7 @@ GEM
40
40
 
41
41
  PLATFORMS
42
42
  java
43
+ ruby
43
44
 
44
45
  DEPENDENCIES
45
46
  activerecord-jdbcteradata-adapter!
data/README.md CHANGED
@@ -5,4 +5,7 @@
5
5
  ## DESCRIPTION:
6
6
 
7
7
  This is an ActiveRecord driver for Teradata using JDBC running under JRuby.
8
+
8
9
  It is still under heavy development.
10
+
11
+ Right now I am testing this code with the ActiveRecord test suite.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "activerecord-jdbcteradata-adapter"
3
- s.version = "0.3.0"
3
+ s.version = "0.3.1"
4
4
  s.authors = ["Chris Parker"]
5
5
  s.email = [ "mrcsparker@gmail.com"]
6
6
  s.homepage = "https://github.com/mrcsparker/activerecord-jdbcteradata-adapter"
@@ -25,7 +25,6 @@ module Arel
25
25
  else
26
26
  sql = super
27
27
  end
28
-
29
28
  sql
30
29
  end
31
30
 
@@ -53,9 +52,9 @@ module Arel
53
52
  primary_key = @connection.columns(table_name).detect { |column| column.primary }
54
53
  return primary_key.name if primary_key
55
54
  # Look for an id column. Return it, without changing case, to cover dbs with a case-sensitive collation.
56
- columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
55
+ @connection.columns(table_name).each { |column| return column.name if column.name =~ /^id$/i }
57
56
  # Give up and provide something which is going to crash almost certainly
58
- columns(table_name)[0].name
57
+ @connection.columns(table_name)[0].name
59
58
  end
60
59
 
61
60
  def add_limit_offset!(sql, options)
@@ -7,6 +7,8 @@ module ::ArJdbc
7
7
  [ /teradata/i, lambda { |cfg, column| column.extend(::ArJdbc::Teradata::Column) } ]
8
8
  end
9
9
 
10
+ ## ActiveRecord::ConnectionAdapters::JdbcAdapter
11
+
10
12
  #- jdbc_connection_class
11
13
 
12
14
  #- jdbc_column_class
@@ -16,6 +18,19 @@ module ::ArJdbc
16
18
  #- adapter_spec
17
19
 
18
20
  #+ modify_types
21
+ def modify_types(types)
22
+ super(types)
23
+ types[:primary_key] = 'INTEGER PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1 MINVALUE -2147483647 MAXVALUE 1000000000 NO CYCLE)',
24
+ types[:string][:limit] = 255
25
+ types[:integer][:limit] = nil
26
+ types
27
+ end
28
+
29
+ # Make sure that integer gets specified as INTEGER and not INTEGER(11)
30
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
31
+ limit = nil if type.to_sym == :integer
32
+ super(type, limit, precision, scale)
33
+ end
19
34
 
20
35
  #+ adapter_name
21
36
  def adapter_name
@@ -41,7 +56,6 @@ module ::ArJdbc
41
56
 
42
57
  #+ native_database_types
43
58
  def native_database_types
44
-
45
59
  super.merge({
46
60
  :primary_key => 'INTEGER PRIMARY KEY NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1 MINVALUE -2147483647 MAXVALUE 1000000000 NO CYCLE)',
47
61
  :string => { :name => 'VARCHAR', :limit => 255 },
@@ -50,15 +64,18 @@ module ::ArJdbc
50
64
  :decimal => { :name => "DECIMAL" },
51
65
  :datetime => { :name => "TIMESTAMP" },
52
66
  :timestamp => { :name => "TIMESTAMP" },
53
- :time => { :name => "TIME" },
67
+ :time => { :name => "TIMESTAMP" },
54
68
  :date => { :name => "DATE" },
55
69
  :binary => { :name => "BLOB" },
56
- :boolean => { :name => "BYTEINT", :limit => 1 },
70
+ :boolean => { :name => "BYTEINT" },
57
71
  :raw => { :name => "BYTE" }
58
72
  })
59
73
  end
60
74
 
61
75
  #- database_name
76
+ def database_name
77
+ @connection.config[:database]
78
+ end
62
79
 
63
80
  #- native_sql_to_type
64
81
 
@@ -96,12 +113,12 @@ module ::ArJdbc
96
113
  output = nil
97
114
  pk = primary_key(table)
98
115
  if pk
99
- output = execute("SELECT TOP 1 #{pk} FROM #{table} ORDER BY #{pk} DESC").first[pk]
116
+ output = execute("SELECT TOP 1 #{quote_column_name(pk)} FROM #{quote_table_name(table)} ORDER BY #{quote_column_name(pk)} DESC").first[pk]
100
117
  end
101
118
  output
102
119
  end
103
120
 
104
- #- select
121
+ #- select
105
122
 
106
123
  #- select_rows
107
124
 
@@ -110,6 +127,11 @@ module ::ArJdbc
110
127
  #- tables
111
128
 
112
129
  #- table_exists?
130
+ def table_exists?(table_name)
131
+ return false unless table_name
132
+ output = execute("SELECT count(*) as table_count FROM dbc.tables WHERE TableName = '#{table_name}' AND DatabaseName = '#{database_name}'")
133
+ output.first['table_count'] > 0
134
+ end
113
135
 
114
136
  #+ indexes
115
137
  # TODO: Multiple indexes per column
@@ -118,7 +140,7 @@ module ::ArJdbc
118
140
  result = select_rows("SELECT" <<
119
141
  " DatabaseName, TableName, ColumnName, IndexType, IndexName, UniqueFlag" <<
120
142
  " FROM DBC.Indices" <<
121
- " WHERE TableName = '#{table_name}'")
143
+ " WHERE TableName = '#{table_name}' AND DatabaseName = '#{database_name}'")
122
144
 
123
145
  result.map do |row|
124
146
  idx_database_name = row[0].to_s.strip
@@ -155,24 +177,142 @@ module ::ArJdbc
155
177
 
156
178
  #- to_sql
157
179
 
180
+ ## ConnectionAdapters::Abstract::SchemaStatements
181
+
182
+ #- table_exists?
183
+
184
+ #- index_exists?
185
+
186
+ #- columns
187
+
188
+ #- column_exists?
189
+
190
+ #- create_table
191
+
192
+ #- change_table
193
+
194
+ #+ rename_table
195
+
196
+ #- drop_table
197
+
198
+ #- add_column
199
+
200
+ #- remove_column
201
+ def remove_column(table_name, *column_names) #:nodoc:
202
+ for column_name in column_names.flatten
203
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
204
+ end
205
+ end
206
+
207
+ #+ change_column
208
+ # This only works in a VERY limited fashion. For example, VARCHAR columns
209
+ # cannot be shortened, one column type cannot be converted to another.
210
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
211
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
212
+ "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
213
+ add_column_options!(change_column_sql, options)
214
+ execute(change_column_sql)
215
+ end
216
+
217
+ #+ change_column_default
218
+ def change_column_default(table_name, column_name, default) #:nodoc:
219
+ execute "ALTER TABLE #{quote_table_name(table_name)} " +
220
+ "ADD #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
221
+ end
222
+
223
+ #+ rename_column
224
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
225
+ execute "ALTER TABLE #{quote_table_name(table_name)} " <<
226
+ "RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
227
+ end
228
+
229
+ #- add_index
230
+
231
+ #- remove_index
232
+
233
+ #- rename_index
234
+
235
+ #- index_name
236
+
237
+ #- index_name_exists?
238
+
239
+ #+ structure_dump
240
+
241
+ #- dump_schema_information
242
+
243
+ #- initialize_schema_migrations_table
244
+
245
+ #- assume_migrated_upto_version
246
+
247
+ #- type_to_sql
248
+
249
+ #- add_column_options!
250
+
251
+ #- distinct
252
+
253
+ #- add_timestamps
254
+
255
+ #- remove_timestamps
256
+
158
257
  module Column
159
258
  # Maps Teradata types of logical Rails types
160
259
  def simplified_type(field_type)
161
260
  case field_type
162
261
  when /^timestamp with(?:out)? time zone$/ then :datetime
262
+ when /byteint/i then :boolean
163
263
  else
164
264
  super
165
265
  end
166
266
  end
167
267
  end # column
168
268
 
269
+ def type_cast
270
+ return super unless value == true || value == false
271
+
272
+ value ? 1 : 0
273
+ end
274
+
275
+ def quote(value, column = nil)
276
+ return value.quoted_id if value.respond_to?(:quoted_id)
277
+ case value
278
+ when String
279
+ %Q{'#{quote_string(value)}'}
280
+ when TrueClass
281
+ '1'
282
+ when FalseClass
283
+ '0'
284
+ else super
285
+ end
286
+ end
287
+
169
288
  def quote_column_name(name)
170
289
  %Q("#{name}")
171
290
  end
172
291
 
173
292
  def quote_table_name(name)
174
- name.to_s
293
+ %Q("#{name.to_s}")
294
+ end
295
+
296
+ def quote_true
297
+ '1'
175
298
  end
176
299
 
300
+ def quoted_false
301
+ '0'
302
+ end
303
+
304
+ def add_index(table_name, column_name, options = {})
305
+ index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
306
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} (#{index_columns}) ON #{quote_table_name(table_name)}"
307
+ end
308
+
309
+ IDENTIFIER_LENGTH = 30 # :nodoc:
310
+
311
+ # maximum length of Teradata identifiers is 30
312
+ def table_alias_length; IDENTIFIER_LENGTH; end # :nodoc:
313
+ def table_name_length; IDENTIFIER_LENGTH; end # :nodoc:
314
+ def index_name_length; IDENTIFIER_LENGTH; end # :nodoc:
315
+ def column_name_length; IDENTIFIER_LENGTH; end # :nodoc:
316
+
177
317
  end
178
318
  end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'models/active_record_schema'
4
+
5
+ require 'models/active_record_models'
6
+
7
+ describe 'ActiveRecordSchemaSpec' do
8
+
9
+ it 'should load in the activerecord sample schema.rb file' do
10
+ expect {
11
+ CreateActiveRecordSchema.up
12
+ }.to_not raise_error
13
+ end
14
+
15
+ context 'from loaded activerecord data' do
16
+ before(:all) do
17
+ CreateActiveRecordSchema.up
18
+ Topic.delete_all
19
+
20
+ 1.upto(4) do |i|
21
+ @topic = Topic.new
22
+ @topic.title = "foo#{i}"
23
+ @topic.author_name = "author#{i}"
24
+ @topic.author_email_address = "email#{i}@localhost"
25
+ @topic.approved = true
26
+ @topic.save
27
+ end
28
+ end
29
+
30
+ it 'should be able to group data' do
31
+ topic = Topic.last
32
+ topic.approved = false
33
+ topic.save
34
+ approved_topics_count = Topic.group(:approved).count(:author_name)[true]
35
+ approved_topics_count.should == 3
36
+ end
37
+ end
38
+ end
data/spec/adapter_spec.rb CHANGED
@@ -19,6 +19,10 @@ describe 'Adapter' do
19
19
  it '#native_database_types' do
20
20
  @adapter.native_database_types.count.should > 0
21
21
  end
22
+
23
+ it '#database_name' do
24
+ @adapter.database_name.should == 'weblog_development'
25
+ end
22
26
 
23
27
  it '#active?' do
24
28
  @adapter.active?.should be_true
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ require 'models/purchase_orders'
4
+
5
+ describe 'AssociationsSpec' do
6
+ before(:all) do
7
+ CreateVendors.up
8
+ CreateProducts.up
9
+ CreatePurchaseOrders.up
10
+ end
11
+
12
+ it 'should create all of the tables' do
13
+ vendor = Vendor.new(:name => 'Test vendor', :catch_phrase => 'Hello, world')
14
+ vendor.products.build(:name => 'Test product', :price => 100.00)
15
+ vendor.save
16
+
17
+ vendor.products.count.should == 1
18
+
19
+ purchase_order = PurchaseOrder.new
20
+ purchase_order.product = vendor.products.first
21
+
22
+ purchase_order.code = "ORDER1"
23
+ purchase_order.quantity = 2
24
+ purchase_order.save
25
+
26
+ vendor.purchase_orders.count.should == 1
27
+ end
28
+
29
+ after(:all) do
30
+ CreateVendors.down
31
+ CreateProducts.down
32
+ CreatePurchaseOrders.down
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ class Topic < ActiveRecord::Base
2
+ self.table_name = 'ar_topics'
3
+ end