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 +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
|