ActiveRecord-JDBC 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -0
- data/install.rb +25 -25
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +151 -57
- data/lib/active_record/connection_adapters/jdbc_adapter_spec.rb +1133 -0
- data/lib/jdbc_adapter.rb +1 -0
- data/test/manualTestDatabase.rb +195 -0
- metadata +10 -6
data/LICENSE
CHANGED
data/install.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
|
2
|
-
require 'fileutils'
|
3
|
-
|
4
|
-
from_d=File.expand_path(File.join(File.dirname(__FILE__),'lib','active_record'))
|
5
|
-
to_d=File.expand_path(File.join(RAILS_ROOT,'lib','active_record'))
|
6
|
-
|
7
|
-
FileUtils.cp_r from_d, to_d
|
8
|
-
|
9
|
-
env_file = File.expand_path(File.join(RAILS_ROOT,"config","environment.rb"))
|
10
|
-
bck_file = File.expand_path(File.join(RAILS_ROOT,"config","~.environment.rb.before_jdbc"))
|
11
|
-
|
12
|
-
FileUtils.mv env_file,bck_file
|
13
|
-
|
14
|
-
File.open(bck_file,"r") {|inf|
|
15
|
-
File.open(env_file,"w") {|out|
|
16
|
-
inf.each_line do |ln|
|
17
|
-
if ln =~ /^Rails::Initializer\.run/
|
18
|
-
out.puts "# Added by ActiveRecord JDBC plugin"
|
19
|
-
out.puts "
|
20
|
-
out.puts
|
21
|
-
end
|
22
|
-
out.puts ln
|
23
|
-
end
|
24
|
-
}
|
25
|
-
}
|
1
|
+
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
from_d=File.expand_path(File.join(File.dirname(__FILE__),'lib','active_record'))
|
5
|
+
to_d=File.expand_path(File.join(RAILS_ROOT,'lib','active_record'))
|
6
|
+
|
7
|
+
FileUtils.cp_r from_d, to_d
|
8
|
+
|
9
|
+
env_file = File.expand_path(File.join(RAILS_ROOT,"config","environment.rb"))
|
10
|
+
bck_file = File.expand_path(File.join(RAILS_ROOT,"config","~.environment.rb.before_jdbc"))
|
11
|
+
|
12
|
+
FileUtils.mv env_file,bck_file
|
13
|
+
|
14
|
+
File.open(bck_file,"r") {|inf|
|
15
|
+
File.open(env_file,"w") {|out|
|
16
|
+
inf.each_line do |ln|
|
17
|
+
if ln =~ /^Rails::Initializer\.run/
|
18
|
+
out.puts "# Added by ActiveRecord JDBC plugin"
|
19
|
+
out.puts "require 'jdbc_adapter'"
|
20
|
+
out.puts
|
21
|
+
end
|
22
|
+
out.puts ln
|
23
|
+
end
|
24
|
+
}
|
25
|
+
}
|
@@ -1,10 +1,20 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'active_record/connection_adapters/jdbc_adapter_spec'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
class Base
|
5
6
|
def self.jdbc_connection(config)
|
6
7
|
ConnectionAdapters::JdbcAdapter.new(ConnectionAdapters::JdbcConnection.new(config), logger, config)
|
7
8
|
end
|
9
|
+
|
10
|
+
alias :attributes_with_quotes_pre_oracle :attributes_with_quotes
|
11
|
+
def attributes_with_quotes(include_primary_key = true) #:nodoc:
|
12
|
+
aq = attributes_with_quotes_pre_oracle(include_primary_key)
|
13
|
+
if connection.class == ConnectionAdapters::JdbcAdapter && connection.is_a?(JdbcSpec::Oracle)
|
14
|
+
aq[self.class.primary_key] = "?" if include_primary_key && aq[self.class.primary_key].nil?
|
15
|
+
end
|
16
|
+
aq
|
17
|
+
end
|
8
18
|
end
|
9
19
|
|
10
20
|
module ConnectionAdapters
|
@@ -29,25 +39,37 @@ module ActiveRecord
|
|
29
39
|
# type left. If all the selectors are applied and there is still more than one
|
30
40
|
# type, an exception will be raised.
|
31
41
|
AR_TO_JDBC_TYPES = {
|
32
|
-
:string => [
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
:
|
48
|
-
|
49
|
-
|
50
|
-
:
|
42
|
+
:string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type']},
|
43
|
+
lambda {|r| r['type_name'] =~ /^varchar/i},
|
44
|
+
lambda {|r| r['type_name'] =~ /^varchar$/i}],
|
45
|
+
:text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'])},
|
46
|
+
lambda {|r| r['type_name'] =~ /^(text|clob)/i} ],
|
47
|
+
:integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type']},
|
48
|
+
lambda {|r| r['type_name'] =~ /^integer$/i},
|
49
|
+
lambda {|r| r['type_name'] =~ /^int4$/i},
|
50
|
+
lambda {|r| r['type_name'] =~ /^int$/i}],
|
51
|
+
:float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE].include?(r['data_type'])},
|
52
|
+
lambda {|r| r['type_name'] =~ /^float/i},
|
53
|
+
lambda {|r| r['type_name'] =~ /^double$/i} ],
|
54
|
+
:datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type']},
|
55
|
+
lambda {|r| r['type_name'] =~ /^datetime/i},
|
56
|
+
lambda {|r| r['type_name'] =~ /^timestamp$/i}],
|
57
|
+
:timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type']},
|
58
|
+
lambda {|r| r['type_name'] =~ /^timestamp$/i},
|
59
|
+
lambda {|r| r['type_name'] =~ /^datetime/i} ],
|
60
|
+
:time => [ lambda {|r| Jdbc::Types::TIME == r['data_type']},
|
61
|
+
lambda {|r| r['type_name'] =~ /^time$/i},
|
62
|
+
lambda {|r| r['type_name'] =~ /^datetime$/i}],
|
63
|
+
:date => [ lambda {|r| Jdbc::Types::DATE == r['data_type']},
|
64
|
+
lambda {|r| r['type_name'] =~ /^datetime$/i}],
|
65
|
+
:binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'])},
|
66
|
+
lambda {|r| r['type_name'] =~ /^blob/i},
|
67
|
+
lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
|
68
|
+
lambda {|r| r['type_name'] =~ /^binary$/i}, ],
|
69
|
+
:boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'])},
|
70
|
+
lambda {|r| r['type_name'] =~ /^bool/i},
|
71
|
+
lambda {|r| r['type_name'] =~ /^tinyint$/i},
|
72
|
+
lambda {|r| r['type_name'] =~ /^decimal$/i}]
|
51
73
|
}
|
52
74
|
|
53
75
|
def initialize(types)
|
@@ -59,7 +81,7 @@ module ActiveRecord
|
|
59
81
|
AR_TO_JDBC_TYPES.each_key do |k|
|
60
82
|
typerow = choose_type(k)
|
61
83
|
type_map[k] = { :name => typerow['type_name'] }
|
62
|
-
type_map[k][:limit] = typerow['precision'] if [:integer
|
84
|
+
type_map[k][:limit] = typerow['precision'] if [:integer, :string].include?(k)
|
63
85
|
type_map[k][:limit] = 1 if k == :boolean
|
64
86
|
end
|
65
87
|
type_map
|
@@ -73,7 +95,7 @@ module ActiveRecord
|
|
73
95
|
return new_types.first if new_types.length == 1
|
74
96
|
types = new_types if new_types.length > 0
|
75
97
|
end
|
76
|
-
raise "unable to choose type from: #{types.collect{|t| t['type_name']}.inspect}"
|
98
|
+
raise "unable to choose type from: #{types.collect{|t| t['type_name']}.inspect} for #{ar_type}"
|
77
99
|
end
|
78
100
|
end
|
79
101
|
|
@@ -90,13 +112,36 @@ module ActiveRecord
|
|
90
112
|
end
|
91
113
|
end
|
92
114
|
|
115
|
+
class JdbcColumn < Column
|
116
|
+
def initialize(config, name, default, *args)
|
117
|
+
case config[:driver].to_s
|
118
|
+
when /oracle/i: self.extend(JdbcSpec::Oracle::Column)
|
119
|
+
when /postgre/i: self.extend(JdbcSpec::PostgreSQL::Column)
|
120
|
+
when /sqlserver|tds/i: self.extend(JdbcSpec::MsSQL::Column)
|
121
|
+
when /hsqldb/i: self.extend(JdbcSpec::HSQLDB::Column)
|
122
|
+
when /derby/i: self.extend(JdbcSpec::Derby::Column)
|
123
|
+
when /db2/i:
|
124
|
+
if config[:url] =~ /^jdbc:derby:net:/
|
125
|
+
self.extend(JdbcSpec::Derby::Column)
|
126
|
+
else
|
127
|
+
self.extend(JdbcSpec::DB2::Column)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
super(name,default_value(default),*args)
|
131
|
+
end
|
132
|
+
|
133
|
+
def default_value(val)
|
134
|
+
val
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
93
138
|
class JdbcConnection
|
94
139
|
def initialize(config)
|
95
|
-
config = config.symbolize_keys
|
96
|
-
driver = config[:driver].to_s
|
97
|
-
user = config[:username].to_s
|
98
|
-
pass = config[:password].to_s
|
99
|
-
url = config[:url].to_s
|
140
|
+
@config = config.symbolize_keys
|
141
|
+
driver = @config[:driver].to_s
|
142
|
+
user = @config[:username].to_s
|
143
|
+
pass = @config[:password].to_s
|
144
|
+
url = @config[:url].to_s
|
100
145
|
|
101
146
|
unless driver && url
|
102
147
|
raise ArgumentError, "jdbc adapter requires driver class and url"
|
@@ -105,29 +150,35 @@ module ActiveRecord
|
|
105
150
|
JdbcDriver.load(driver)
|
106
151
|
@connection = Jdbc::DriverManager.getConnection(url, user, pass)
|
107
152
|
set_native_database_types
|
153
|
+
|
154
|
+
@stmts = {}
|
155
|
+
rescue Exception => e
|
156
|
+
raise "The driver encounter an error: #{e}"
|
108
157
|
end
|
109
158
|
|
159
|
+
def ps(sql)
|
160
|
+
@connection.prepareStatement(sql)
|
161
|
+
end
|
162
|
+
|
110
163
|
def set_native_database_types
|
111
164
|
types = unmarshal_result(@connection.getMetaData.getTypeInfo)
|
112
165
|
@native_types = JdbcTypeConverter.new(types).choose_best_types
|
113
166
|
end
|
114
167
|
|
115
|
-
def native_database_types
|
116
|
-
types = {
|
117
|
-
# TODO: this is copied from MySQL -- figure out how to
|
118
|
-
# generalize the primary key type
|
119
|
-
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
120
|
-
}
|
168
|
+
def native_database_types(adapt)
|
169
|
+
types = {}
|
121
170
|
@native_types.each_pair {|k,v| types[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => kv.last.dup})}}
|
122
|
-
types
|
171
|
+
adapt.modify_types(types)
|
123
172
|
end
|
124
|
-
|
173
|
+
|
125
174
|
def columns(table_name, name = nil)
|
126
175
|
metadata = @connection.getMetaData
|
176
|
+
table_name.upcase! if metadata.storesUpperCaseIdentifiers
|
177
|
+
table_name.downcase! if metadata.storesLowerCaseIdentifiers
|
127
178
|
results = metadata.getColumns(nil, nil, table_name, nil)
|
128
179
|
columns = []
|
129
180
|
unmarshal_result(results).each do |col|
|
130
|
-
columns << ActiveRecord::ConnectionAdapters::
|
181
|
+
columns << ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config,col['column_name'].downcase, col['column_def'],
|
131
182
|
"#{col['type_name']}(#{col['column_size']})", col['is_nullable'] != 'NO')
|
132
183
|
end
|
133
184
|
columns
|
@@ -141,7 +192,7 @@ module ActiveRecord
|
|
141
192
|
|
142
193
|
def execute_insert(sql, pk)
|
143
194
|
stmt = @connection.createStatement
|
144
|
-
stmt.executeUpdate(sql,
|
195
|
+
stmt.executeUpdate(sql,Jdbc::Statement::RETURN_GENERATED_KEYS)
|
145
196
|
row = unmarshal_result(stmt.getGeneratedKeys)
|
146
197
|
row.first && row.first.values.first
|
147
198
|
ensure
|
@@ -218,18 +269,22 @@ module ActiveRecord
|
|
218
269
|
decimal.to_f
|
219
270
|
else
|
220
271
|
case type
|
221
|
-
when Jdbc::Types::CHAR, Jdbc::Types::VARCHAR, Jdbc::Types::LONGVARCHAR
|
272
|
+
when Jdbc::Types::CHAR, Jdbc::Types::VARCHAR, Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB
|
222
273
|
resultset.getString(row)
|
223
274
|
when Jdbc::Types::SMALLINT, Jdbc::Types::INTEGER, Jdbc::Types::NUMERIC, Jdbc::Types::BIGINT
|
224
275
|
resultset.getInt(row)
|
225
|
-
when Jdbc::Types::BIT, Jdbc::Types::BOOLEAN, Jdbc::Types::TINYINT
|
276
|
+
when Jdbc::Types::BIT, Jdbc::Types::BOOLEAN, Jdbc::Types::TINYINT, Jdbc::Types::DECIMAL
|
226
277
|
resultset.getBoolean(row)
|
278
|
+
when Jdbc::Types::FLOAT, Jdbc::Types::DOUBLE
|
279
|
+
resultset.getDouble(row)
|
227
280
|
when Jdbc::Types::TIMESTAMP
|
228
281
|
to_ruby_time(resultset.getTimestamp(row))
|
229
282
|
when Jdbc::Types::TIME
|
230
283
|
to_ruby_time(resultset.getTime(row))
|
231
284
|
when Jdbc::Types::DATE
|
232
285
|
to_ruby_time(resultset.getDate(row))
|
286
|
+
when Jdbc::Types::LONGVARBINARY, Jdbc::Types::BLOB, Jdbc::Types::BINARY
|
287
|
+
resultset.getString(row)
|
233
288
|
else
|
234
289
|
types = Jdbc::Types.constants
|
235
290
|
name = types.find {|t| Jdbc::Types.const_get(t.to_sym) == type}
|
@@ -243,8 +298,27 @@ module ActiveRecord
|
|
243
298
|
def initialize(connection, logger, config)
|
244
299
|
super(connection, logger)
|
245
300
|
@config = config
|
301
|
+
case config[:driver].to_s
|
302
|
+
when /oracle/i: self.extend(JdbcSpec::Oracle)
|
303
|
+
when /postgre/i: self.extend(JdbcSpec::PostgreSQL)
|
304
|
+
when /mysql/i: self.extend(JdbcSpec::MySQL)
|
305
|
+
when /sqlserver|tds/i: self.extend(JdbcSpec::MsSQL)
|
306
|
+
when /hsqldb/i: self.extend(JdbcSpec::HSQLDB)
|
307
|
+
when /derby/i: self.extend(JdbcSpec::Derby)
|
308
|
+
when /db2/i:
|
309
|
+
if config[:url] =~ /^jdbc:derby:net:/
|
310
|
+
self.extend(JdbcSpec::Derby)
|
311
|
+
else
|
312
|
+
self.extend(JdbcSpec::DB2)
|
313
|
+
end
|
314
|
+
when /firebird/i: self.extend(JdbcSpec::FireBird)
|
315
|
+
end
|
246
316
|
end
|
247
317
|
|
318
|
+
def modify_types(tp)
|
319
|
+
tp
|
320
|
+
end
|
321
|
+
|
248
322
|
def adapter_name #:nodoc:
|
249
323
|
'JDBC'
|
250
324
|
end
|
@@ -254,16 +328,51 @@ module ActiveRecord
|
|
254
328
|
end
|
255
329
|
|
256
330
|
def native_database_types #:nodoc
|
257
|
-
@connection.native_database_types
|
331
|
+
@connection.native_database_types(self)
|
258
332
|
end
|
259
333
|
|
334
|
+
def native_sql_to_type(tp)
|
335
|
+
if /^(.*?)\(([0-9]+)\)/ =~ tp
|
336
|
+
tname = $1
|
337
|
+
limit = $2.to_i
|
338
|
+
ntype = native_database_types
|
339
|
+
if ntype[:primary_key] == tp
|
340
|
+
return :primary_key,nil
|
341
|
+
else
|
342
|
+
ntype.each do |name,val|
|
343
|
+
if name == :primary_key
|
344
|
+
next
|
345
|
+
end
|
346
|
+
if val[:name].downcase == tname.downcase && (val[:limit].nil? || val[:limit].to_i == limit)
|
347
|
+
return name,limit
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
elsif /^(.*?)/ =~ tp
|
352
|
+
tname = $1
|
353
|
+
ntype = native_database_types
|
354
|
+
if ntype[:primary_key] == tp
|
355
|
+
return :primary_key,nil
|
356
|
+
else
|
357
|
+
ntype.each do |name,val|
|
358
|
+
if val[:name].downcase == tname.downcase && val[:limit].nil?
|
359
|
+
return name,nil
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
else
|
364
|
+
return :string,255
|
365
|
+
end
|
366
|
+
return nil,nil
|
367
|
+
end
|
368
|
+
|
260
369
|
def active?
|
261
370
|
true
|
262
371
|
end
|
263
372
|
|
264
373
|
def reconnect!
|
265
374
|
@connection.close rescue nil
|
266
|
-
@connection = JdbcConnection.new(@config)
|
375
|
+
@connection = JdbcConnection.new(@config,self)
|
267
376
|
end
|
268
377
|
|
269
378
|
def select_all(sql, name = nil)
|
@@ -276,7 +385,7 @@ module ActiveRecord
|
|
276
385
|
|
277
386
|
def execute(sql, name = nil)
|
278
387
|
log_no_bench(sql, name) do
|
279
|
-
if sql =~ /^select/i
|
388
|
+
if sql =~ /^(select|show)/i
|
280
389
|
@connection.execute_query(sql)
|
281
390
|
else
|
282
391
|
@connection.execute_update(sql)
|
@@ -284,21 +393,6 @@ module ActiveRecord
|
|
284
393
|
end
|
285
394
|
end
|
286
395
|
|
287
|
-
# Oracle doesn't support limit and offset. Fake it instead.
|
288
|
-
def add_limit_offset!(sql, options) #:nodoc:
|
289
|
-
if /oracle/ =~ @config[:driver]
|
290
|
-
offset = options[:offset] || 0
|
291
|
-
|
292
|
-
if limit = options[:limit]
|
293
|
-
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
|
294
|
-
elsif offset > 0
|
295
|
-
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
|
296
|
-
end
|
297
|
-
else
|
298
|
-
super
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
396
|
alias :update :execute
|
303
397
|
alias :delete :execute
|
304
398
|
|
@@ -330,7 +424,7 @@ module ActiveRecord
|
|
330
424
|
end
|
331
425
|
|
332
426
|
private
|
333
|
-
def select(sql, name)
|
427
|
+
def select(sql, name=nil)
|
334
428
|
log_no_bench(sql, name) { @connection.execute_query(sql) }
|
335
429
|
end
|
336
430
|
|