ActiveRecord-JDBC 0.0.1 → 0.2.0
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/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
|
|