activerecord-jdbcteradata-adapter 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +4 -3
- data/README.md +3 -0
- data/activerecord-jdbcteradata-adapter.gemspec +1 -1
- data/lib/arel/visitors/teradata.rb +2 -3
- data/lib/arjdbc/teradata/adapter.rb +147 -7
- data/spec/active_record_schema_spec.rb +38 -0
- data/spec/adapter_spec.rb +4 -0
- data/spec/associations_spec.rb +34 -0
- data/spec/models/active_record_models.rb +3 -0
- data/spec/models/active_record_schema.rb +732 -0
- data/spec/models/purchase_orders.rb +59 -0
- metadata +70 -72
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
activerecord-jdbcteradata-adapter (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.
|
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.
|
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
@@ -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.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 => "
|
67
|
+
:time => { :name => "TIMESTAMP" },
|
54
68
|
:date => { :name => "DATE" },
|
55
69
|
:binary => { :name => "BLOB" },
|
56
|
-
:boolean => { :name => "BYTEINT"
|
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
|