ActiveRecord-JDBC 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +13 -0
- data/Manifest.txt +41 -0
- data/README.txt +94 -0
- data/Rakefile +55 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +251 -84
- data/lib/active_record/connection_adapters/jndi_adapter.rb +51 -0
- data/lib/jdbc_adapter.rb +9 -1
- data/lib/jdbc_adapter/jdbc_derby.rb +66 -6
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +50 -2
- data/lib/jdbc_adapter/jdbc_mysql.rb +3 -12
- data/test/activerecord/connection_adapters/type_conversion_test.rb +30 -0
- data/test/db/derby.rb +21 -0
- data/test/db/h2.rb +14 -0
- data/test/db/hsqldb.rb +5 -0
- data/test/derby_simple_test.rb +16 -0
- data/test/h2_simple_test.rb +9 -0
- data/test/hsqldb_simple_test.rb +1 -1
- data/test/minirunit/testH2.rb +73 -0
- data/test/models/auto_id.rb +19 -0
- data/test/models/data_types.rb +19 -0
- data/test/models/entry.rb +3 -4
- data/test/mysql_simple_test.rb +1 -0
- data/test/simple.rb +75 -24
- metadata +39 -39
- data/init.rb +0 -8
- data/install.rb +0 -25
- data/test/activerecord/jall.sh +0 -7
- data/test/activerecord/jtest.sh +0 -3
data/History.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
== 0.2.3
|
2
|
+
|
3
|
+
* Release coincides (and compatible) with JRuby 0.9.8 release
|
4
|
+
* 8 bugs fixed: see http://rubyurl.com/0Da
|
5
|
+
* Improvements and compatibility fixes for Rails 1.2.x
|
6
|
+
|
7
|
+
== 0.2.1, 0.2.2
|
8
|
+
|
9
|
+
* Early releases, added better support for multiple databases
|
10
|
+
|
11
|
+
== 0.0.1
|
12
|
+
|
13
|
+
* Initial, very alpha release
|
data/Manifest.txt
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
LICENSE
|
6
|
+
lib/jdbc_adapter.rb
|
7
|
+
lib/active_record/connection_adapters/jdbc_adapter.rb
|
8
|
+
lib/active_record/connection_adapters/jdbc_adapter_spec.rb
|
9
|
+
lib/active_record/connection_adapters/jndi_adapter.rb
|
10
|
+
lib/jdbc_adapter/jdbc_db2.rb
|
11
|
+
lib/jdbc_adapter/jdbc_derby.rb
|
12
|
+
lib/jdbc_adapter/jdbc_firebird.rb
|
13
|
+
lib/jdbc_adapter/jdbc_hsqldb.rb
|
14
|
+
lib/jdbc_adapter/jdbc_mimer.rb
|
15
|
+
lib/jdbc_adapter/jdbc_mssql.rb
|
16
|
+
lib/jdbc_adapter/jdbc_mysql.rb
|
17
|
+
lib/jdbc_adapter/jdbc_oracle.rb
|
18
|
+
lib/jdbc_adapter/jdbc_postgre.rb
|
19
|
+
test/derby_simple_test.rb
|
20
|
+
test/h2_simple_test.rb
|
21
|
+
test/hsqldb_simple_test.rb
|
22
|
+
test/manualTestDatabase.rb
|
23
|
+
test/minirunit.rb
|
24
|
+
test/mysql_simple_test.rb
|
25
|
+
test/simple.rb
|
26
|
+
test/activerecord/connection_adapters/type_conversion_test.rb
|
27
|
+
test/activerecord/connections/native_jdbc_mysql/connection.rb
|
28
|
+
test/db/derby.rb
|
29
|
+
test/db/h2.rb
|
30
|
+
test/db/hsqldb.rb
|
31
|
+
test/db/logger.rb
|
32
|
+
test/db/mysql.rb
|
33
|
+
test/minirunit/testConnect.rb
|
34
|
+
test/minirunit/testH2.rb
|
35
|
+
test/minirunit/testHsqldb.rb
|
36
|
+
test/minirunit/testLoadActiveRecord.rb
|
37
|
+
test/minirunit/testMysql.rb
|
38
|
+
test/minirunit/testRawSelect.rb
|
39
|
+
test/models/auto_id.rb
|
40
|
+
test/models/data_types.rb
|
41
|
+
test/models/entry.rb
|
data/README.txt
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
ActiveRecord-JDBC is a database adapter for Rails' ActiveRecord component that can be used with JRuby[http://www.jruby.org/]. It allows use of virtually any JDBC-compliant database with your JRuby on Rails application.
|
2
|
+
|
3
|
+
ActiveRecord JDBC is a sub-project of jruby-extras at RubyForge.
|
4
|
+
|
5
|
+
== Databases
|
6
|
+
|
7
|
+
What's there, and what is not there:
|
8
|
+
|
9
|
+
* MySQL - Complete support
|
10
|
+
* PostgreSQL - Complete support
|
11
|
+
* Oracle - Complete support
|
12
|
+
* Microsoft SQL Server - Complete support except for change_column_default
|
13
|
+
* DB2 - Complete, except for the migrations:
|
14
|
+
* change_column
|
15
|
+
* change_column_default
|
16
|
+
* remove_column
|
17
|
+
* rename_column
|
18
|
+
* add_index
|
19
|
+
* remove_index
|
20
|
+
* rename_table
|
21
|
+
* FireBird - Complete, except for change_column_default and rename_column
|
22
|
+
* Derby - Complete, except for:
|
23
|
+
* change_column
|
24
|
+
* change_column_default
|
25
|
+
* remove_column
|
26
|
+
* rename_column
|
27
|
+
* HSQLDB - Complete
|
28
|
+
|
29
|
+
Other databases will require testing and likely a custom configuration module. Please join the
|
30
|
+
jruby-extras mailing-list[http://rubyforge.org/mail/?group_id=2014] to help us discover support for more databases.
|
31
|
+
|
32
|
+
== Using ActiveRecord JDBC
|
33
|
+
|
34
|
+
=== Standalone, with ActiveRecord
|
35
|
+
|
36
|
+
1. Install the gem with JRuby:
|
37
|
+
jruby --command gem install ActiveRecord-JDBC
|
38
|
+
2. Ensure the following code gets executed in your script:
|
39
|
+
require 'rubygems'
|
40
|
+
gem 'ActiveRecord-JDBC'
|
41
|
+
require 'jdbc_adapter'
|
42
|
+
require 'active_record'
|
43
|
+
|
44
|
+
3. After this you can establish a JDBC connection like this:
|
45
|
+
ActiveRecord::Base.establish_connection(
|
46
|
+
:adapter => 'jdbc',
|
47
|
+
:driver => 'org.apache.derby.jdbc.EmbeddedDriver',
|
48
|
+
:url => 'jdbc:derby:test_ar;create=true'
|
49
|
+
)
|
50
|
+
|
51
|
+
Provided you have the derby libraries in your classpath, this is enough
|
52
|
+
to establish an in-memory JDBC connection. The required parameters to
|
53
|
+
establish_connection for ActiveRecord JDBC are:
|
54
|
+
|
55
|
+
* adapter
|
56
|
+
* driver
|
57
|
+
* url
|
58
|
+
|
59
|
+
If provided, password and username will be used. After the connection is established
|
60
|
+
Active Record can be used as usual.
|
61
|
+
|
62
|
+
=== Inside Rails
|
63
|
+
|
64
|
+
To use ActiveRecord-JDBC with JRuby on Rails:
|
65
|
+
|
66
|
+
1. Install the gem with JRuby:
|
67
|
+
jruby --command gem install ActiveRecord-JDBC
|
68
|
+
2. Add one-time setup to your config/environment.rb file in your Rails application. Add the following lines just before the <code>Rails::Initializer</code>.
|
69
|
+
require 'rubygems'
|
70
|
+
gem 'ActiveRecord-JDBC'
|
71
|
+
require 'jdbc_adapter'
|
72
|
+
3. Configure your database.yml to use the <code>jdbc</code> adapter. For now, you'll need to know the database driver class and URL. Example:
|
73
|
+
development:
|
74
|
+
adapter: jdbc
|
75
|
+
username: blog
|
76
|
+
password:
|
77
|
+
driver: com.mysql.jdbc.Driver
|
78
|
+
url: jdbc:mysql://localhost:3306/weblog_development
|
79
|
+
|
80
|
+
== Running AR-JDBC's Tests
|
81
|
+
|
82
|
+
By default hsql, mysql, and derby are run. In order to run all tests you
|
83
|
+
must download each of the databases about put their JDBC drivers in your
|
84
|
+
classpath. Here is an example of I use:
|
85
|
+
|
86
|
+
CLASSPATH=~/opt/derby/lib/derby.jar:~/opt/mysql/mysql-connector-java-3.1.14-bin.jar:~/opt/hsqldb/lib/hsqldb.jar jruby ../jruby/bin/jruby --command rake
|
87
|
+
|
88
|
+
== Authors
|
89
|
+
|
90
|
+
This project was written by Nick Sieger <nick@nicksieger.com> and Ola Bini <ola@ologix.com> with lots of help from the JRuby community.
|
91
|
+
|
92
|
+
== License
|
93
|
+
|
94
|
+
ActiveRecord-JDBC is released under a BSD license. See the LICENSE file included with the distribution for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
task :default => :test
|
5
|
+
|
6
|
+
desc "Run AR-JDBC tests"
|
7
|
+
if RUBY_PLATFORM =~ /java/
|
8
|
+
task :test => [:test_mysql, :test_hsqldb, :test_derby]
|
9
|
+
else
|
10
|
+
task :test => [:test_mysql]
|
11
|
+
end
|
12
|
+
|
13
|
+
Rake::TestTask.new(:test_mysql) do |t|
|
14
|
+
t.test_files = FileList['test/mysql_simple_test.rb']
|
15
|
+
t.libs << 'test'
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::TestTask.new(:test_hsqldb) do |t|
|
19
|
+
t.test_files = FileList['test/hsqldb_simple_test.rb']
|
20
|
+
t.libs << 'test'
|
21
|
+
end
|
22
|
+
|
23
|
+
Rake::TestTask.new(:test_derby) do |t|
|
24
|
+
t.test_files = FileList['test/derby_simple_test.rb',
|
25
|
+
'test/activerecord/connection_adapters/type_conversion_test.rb']
|
26
|
+
t.libs << 'test'
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'hoe'
|
31
|
+
|
32
|
+
MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt",
|
33
|
+
"Rakefile", "LICENSE", "lib/**/*.rb", "test/**/*.rb"]
|
34
|
+
|
35
|
+
Hoe.new("ActiveRecord-JDBC", "0.2.3") do |p|
|
36
|
+
p.rubyforge_name = "jruby-extras"
|
37
|
+
p.url = "http://jruby-extras.rubyforge.org/ActiveRecord-JDBC"
|
38
|
+
p.author = "Nick Sieger, Ola Bini and JRuby contributors"
|
39
|
+
p.email = "nick@nicksieger.com, ola.bini@ki.se"
|
40
|
+
p.summary = "JDBC adapter for ActiveRecord, for use within JRuby on Rails."
|
41
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
42
|
+
p.description = p.paragraphs_of('README.txt', 0...1).join("\n\n")
|
43
|
+
p.extra_deps.reject!{|d| d.first == "hoe"}
|
44
|
+
end.spec.files = MANIFEST
|
45
|
+
|
46
|
+
# Automated manifest
|
47
|
+
task :manifest do
|
48
|
+
File.open("Manifest.txt", "w") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
|
49
|
+
end
|
50
|
+
|
51
|
+
task :package => :manifest
|
52
|
+
rescue => e
|
53
|
+
# Install hoe in order to make a release
|
54
|
+
# puts e.inspect
|
55
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'java'
|
2
3
|
require 'active_record/connection_adapters/jdbc_adapter_spec'
|
3
4
|
|
4
5
|
module ActiveRecord
|
@@ -19,16 +20,36 @@ module ActiveRecord
|
|
19
20
|
|
20
21
|
module ConnectionAdapters
|
21
22
|
module Java
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
Class = java.lang.Class
|
24
|
+
URL = java.net.URL
|
25
|
+
URLClassLoader = java.net.URLClassLoader
|
25
26
|
end
|
26
27
|
|
27
28
|
module Jdbc
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
DriverManager = java.sql.DriverManager
|
30
|
+
Statement = java.sql.Statement
|
31
|
+
Types = java.sql.Types
|
32
|
+
|
33
|
+
# some symbolic constants for the benefit of the JDBC-based
|
34
|
+
# JdbcConnection#indexes method
|
35
|
+
module IndexMetaData
|
36
|
+
INDEX_NAME = 6
|
37
|
+
NON_UNIQUE = 4
|
38
|
+
TABLE_NAME = 3
|
39
|
+
COLUMN_NAME = 9
|
40
|
+
end
|
41
|
+
|
42
|
+
module TableMetaData
|
43
|
+
TABLE_CAT = 1
|
44
|
+
TABLE_SCHEM = 2
|
45
|
+
TABLE_NAME = 3
|
46
|
+
TABLE_TYPE = 4
|
47
|
+
end
|
48
|
+
|
49
|
+
module PrimaryKeyMetaData
|
50
|
+
COLUMN_NAME = 4
|
51
|
+
end
|
52
|
+
|
32
53
|
end
|
33
54
|
|
34
55
|
# I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
|
@@ -57,6 +78,8 @@ module ActiveRecord
|
|
57
78
|
lambda {|r| r['type_name'] =~ /^integer$/i},
|
58
79
|
lambda {|r| r['type_name'] =~ /^int4$/i},
|
59
80
|
lambda {|r| r['type_name'] =~ /^int$/i}],
|
81
|
+
:decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type']},
|
82
|
+
lambda {|r| r['type_name'] =~ /^decimal$/i}],
|
60
83
|
:float => [ lambda {|r| [Jdbc::Types::FLOAT,Jdbc::Types::DOUBLE].include?(r['data_type'])},
|
61
84
|
lambda {|r| r['type_name'] =~ /^float/i},
|
62
85
|
lambda {|r| r['type_name'] =~ /^double$/i},
|
@@ -71,7 +94,7 @@ module ActiveRecord
|
|
71
94
|
lambda {|r| r['type_name'] =~ /^time$/i},
|
72
95
|
lambda {|r| r['type_name'] =~ /^datetime$/i}],
|
73
96
|
:date => [ lambda {|r| Jdbc::Types::DATE == r['data_type']},
|
74
|
-
lambda {|r| r['type_name'] =~ /^
|
97
|
+
lambda {|r| r['type_name'] =~ /^date$/i}],
|
75
98
|
:binary => [ lambda {|r| [Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB].include?(r['data_type'])},
|
76
99
|
lambda {|r| r['type_name'] =~ /^blob/i},
|
77
100
|
lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
|
@@ -80,7 +103,7 @@ module ActiveRecord
|
|
80
103
|
:boolean => [ lambda {|r| [Jdbc::Types::TINYINT].include?(r['data_type'])},
|
81
104
|
lambda {|r| r['type_name'] =~ /^bool/i},
|
82
105
|
lambda {|r| r['type_name'] =~ /^tinyint$/i},
|
83
|
-
lambda {|r| r['type_name'] =~ /^decimal$/i}]
|
106
|
+
lambda {|r| r['type_name'] =~ /^decimal$/i}],
|
84
107
|
}
|
85
108
|
|
86
109
|
def initialize(types)
|
@@ -91,8 +114,8 @@ module ActiveRecord
|
|
91
114
|
type_map = {}
|
92
115
|
AR_TO_JDBC_TYPES.each_key do |k|
|
93
116
|
typerow = choose_type(k)
|
94
|
-
type_map[k] = { :name => typerow['type_name']
|
95
|
-
type_map[k][:limit] = typerow['precision'] if [:integer, :string].include?(k)
|
117
|
+
type_map[k] = { :name => typerow['type_name'] }
|
118
|
+
type_map[k][:limit] = typerow['precision'] if [:integer, :string, :decimal].include?(k)
|
96
119
|
type_map[k][:limit] = 1 if k == :boolean
|
97
120
|
end
|
98
121
|
type_map
|
@@ -123,28 +146,35 @@ module ActiveRecord
|
|
123
146
|
end
|
124
147
|
|
125
148
|
class JdbcColumn < Column
|
149
|
+
COLUMN_TYPES = {
|
150
|
+
/oracle/i => lambda {|cfg,col| col.extend(JdbcSpec::Oracle::Column)},
|
151
|
+
/postgre/i => lambda {|cfg,col| col.extend(JdbcSpec::PostgreSQL::Column)},
|
152
|
+
/sqlserver|tds/i => lambda {|cfg,col| col.extend(JdbcSpec::MsSQL::Column)},
|
153
|
+
/hsqldb|\.h2\./i => lambda {|cfg,col| col.extend(JdbcSpec::HSQLDB::Column)},
|
154
|
+
/derby/i => lambda {|cfg,col| col.extend(JdbcSpec::Derby::Column)},
|
155
|
+
/db2/i => lambda {|cfg,col|
|
156
|
+
if cfg[:url] =~ /^jdbc:derby:net:/
|
157
|
+
col.extend(JdbcSpec::Derby::Column)
|
158
|
+
else
|
159
|
+
col.extend(JdbcSpec::DB2::Column)
|
160
|
+
end }
|
161
|
+
}
|
162
|
+
|
126
163
|
def initialize(config, name, default, *args)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
when /derby/i: self.extend(JdbcSpec::Derby::Column)
|
133
|
-
when /db2/i:
|
134
|
-
if config[:url] =~ /^jdbc:derby:net:/
|
135
|
-
self.extend(JdbcSpec::Derby::Column)
|
136
|
-
else
|
137
|
-
self.extend(JdbcSpec::DB2::Column)
|
138
|
-
end
|
164
|
+
ds = config[:driver].to_s
|
165
|
+
for reg, func in COLUMN_TYPES
|
166
|
+
if reg === ds
|
167
|
+
func.call(config,self)
|
168
|
+
end
|
139
169
|
end
|
140
170
|
super(name,default_value(default),*args)
|
141
|
-
end
|
171
|
+
end
|
142
172
|
|
143
173
|
def default_value(val)
|
144
174
|
val
|
145
175
|
end
|
146
176
|
end
|
147
|
-
|
177
|
+
|
148
178
|
class JdbcConnection
|
149
179
|
def initialize(config)
|
150
180
|
@config = config.symbolize_keys
|
@@ -169,7 +199,7 @@ module ActiveRecord
|
|
169
199
|
def ps(sql)
|
170
200
|
@connection.prepareStatement(sql)
|
171
201
|
end
|
172
|
-
|
202
|
+
|
173
203
|
def set_native_database_types
|
174
204
|
types = unmarshal_result(@connection.getMetaData.getTypeInfo)
|
175
205
|
@native_types = JdbcTypeConverter.new(types).choose_best_types
|
@@ -177,10 +207,10 @@ module ActiveRecord
|
|
177
207
|
|
178
208
|
def native_database_types(adapt)
|
179
209
|
types = {}
|
180
|
-
@native_types.each_pair {|k,v| types[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => kv.last.dup})}}
|
210
|
+
@native_types.each_pair {|k,v| types[k] = v.inject({}) {|memo,kv| memo.merge({kv.first => (kv.last.dup rescue kv.last)})}}
|
181
211
|
adapt.modify_types(types)
|
182
212
|
end
|
183
|
-
|
213
|
+
|
184
214
|
def columns(table_name, name = nil)
|
185
215
|
metadata = @connection.getMetaData
|
186
216
|
table_name.upcase! if metadata.storesUpperCaseIdentifiers
|
@@ -188,23 +218,112 @@ module ActiveRecord
|
|
188
218
|
results = metadata.getColumns(nil, nil, table_name, nil)
|
189
219
|
columns = []
|
190
220
|
unmarshal_result(results).each do |col|
|
191
|
-
|
192
|
-
|
221
|
+
column_name = col['column_name']
|
222
|
+
column_name = column_name.downcase if metadata.storesUpperCaseIdentifiers
|
223
|
+
precision = col["column_size"]
|
224
|
+
scale = col["decimal_digits"]
|
225
|
+
coltype = col["type_name"]
|
226
|
+
if precision && precision > 0
|
227
|
+
coltype << "(#{precision}"
|
228
|
+
coltype << ",#{scale}" if scale && scale > 0
|
229
|
+
coltype << ")"
|
230
|
+
end
|
231
|
+
columns << ActiveRecord::ConnectionAdapters::JdbcColumn.new(@config, column_name, col['column_def'],
|
232
|
+
coltype, col['is_nullable'] != 'NO')
|
193
233
|
end
|
194
234
|
columns
|
235
|
+
rescue
|
236
|
+
if @connection.is_closed
|
237
|
+
reconnect!
|
238
|
+
retry
|
239
|
+
else
|
240
|
+
raise
|
241
|
+
end
|
195
242
|
end
|
196
243
|
|
197
|
-
def tables
|
244
|
+
def tables(&table_filter)
|
198
245
|
metadata = @connection.getMetaData
|
199
246
|
results = metadata.getTables(nil, nil, nil, nil)
|
200
|
-
unmarshal_result(results).collect {|t| t['table_name']}
|
247
|
+
unmarshal_result(results, &table_filter).collect {|t| t['table_name'].downcase }
|
248
|
+
rescue
|
249
|
+
if @connection.is_closed
|
250
|
+
reconnect!
|
251
|
+
retry
|
252
|
+
else
|
253
|
+
raise
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Get a list of all primary keys associated with the given table
|
258
|
+
def primary_keys(table_name)
|
259
|
+
meta_data = @connection.getMetaData
|
260
|
+
result_set = meta_data.get_primary_keys(nil, nil, table_name.to_s.upcase)
|
261
|
+
key_names = []
|
262
|
+
|
263
|
+
while result_set.next
|
264
|
+
key_names << result_set.get_string(Jdbc::PrimaryKeyMetaData::COLUMN_NAME).downcase
|
265
|
+
end
|
266
|
+
|
267
|
+
key_names
|
268
|
+
end
|
269
|
+
|
270
|
+
# Default JDBC introspection for index metadata on the JdbcConnection.
|
271
|
+
# This is currently used for migrations by JdbcSpec::HSQDLB and JdbcSpec::Derby
|
272
|
+
# indexes with a little filtering tacked on.
|
273
|
+
#
|
274
|
+
# JDBC index metadata is denormalized (multiple rows may be returned for
|
275
|
+
# one index, one row per column in the index), so a simple block-based
|
276
|
+
# filter like that used for tables doesn't really work here. Callers
|
277
|
+
# should filter the return from this method instead.
|
278
|
+
def indexes(table_name, name = nil)
|
279
|
+
metadata = @connection.getMetaData
|
280
|
+
resultset = metadata.getIndexInfo(nil, nil, table_name.to_s.upcase, false, false)
|
281
|
+
primary_keys = primary_keys(table_name)
|
282
|
+
indexes = []
|
283
|
+
current_index = nil
|
284
|
+
while resultset.next
|
285
|
+
index_name = resultset.get_string(Jdbc::IndexMetaData::INDEX_NAME).downcase
|
286
|
+
column_name = resultset.get_string(Jdbc::IndexMetaData::COLUMN_NAME).downcase
|
287
|
+
|
288
|
+
next if primary_keys.include? column_name
|
289
|
+
|
290
|
+
# We are working on a new index
|
291
|
+
if current_index != index_name
|
292
|
+
current_index = index_name
|
293
|
+
table_name = resultset.get_string(Jdbc::IndexMetaData::TABLE_NAME).downcase
|
294
|
+
non_unique = resultset.get_boolean(Jdbc::IndexMetaData::NON_UNIQUE)
|
295
|
+
|
296
|
+
# empty list for column names, we'll add to that in just a bit
|
297
|
+
indexes << IndexDefinition.new(table_name, index_name, !non_unique, [])
|
298
|
+
end
|
299
|
+
|
300
|
+
# One or more columns can be associated with an index
|
301
|
+
indexes.last.columns << column_name
|
302
|
+
end
|
303
|
+
resultset.close
|
304
|
+
indexes
|
305
|
+
rescue
|
306
|
+
if @connection.is_closed
|
307
|
+
reconnect!
|
308
|
+
retry
|
309
|
+
else
|
310
|
+
raise
|
311
|
+
end
|
201
312
|
end
|
202
313
|
|
314
|
+
|
203
315
|
def execute_insert(sql, pk)
|
204
316
|
stmt = @connection.createStatement
|
205
317
|
stmt.executeUpdate(sql,Jdbc::Statement::RETURN_GENERATED_KEYS)
|
206
318
|
row = unmarshal_id_result(stmt.getGeneratedKeys)
|
207
319
|
row.first && row.first.values.first
|
320
|
+
rescue
|
321
|
+
if @connection.is_closed
|
322
|
+
reconnect!
|
323
|
+
retry
|
324
|
+
else
|
325
|
+
raise
|
326
|
+
end
|
208
327
|
ensure
|
209
328
|
stmt.close
|
210
329
|
end
|
@@ -212,6 +331,13 @@ module ActiveRecord
|
|
212
331
|
def execute_update(sql)
|
213
332
|
stmt = @connection.createStatement
|
214
333
|
stmt.executeUpdate(sql)
|
334
|
+
rescue
|
335
|
+
if @connection.is_closed
|
336
|
+
reconnect!
|
337
|
+
retry
|
338
|
+
else
|
339
|
+
raise
|
340
|
+
end
|
215
341
|
ensure
|
216
342
|
stmt.close
|
217
343
|
end
|
@@ -219,6 +345,13 @@ module ActiveRecord
|
|
219
345
|
def execute_query(sql)
|
220
346
|
stmt = @connection.createStatement
|
221
347
|
unmarshal_result(stmt.executeQuery(sql))
|
348
|
+
rescue
|
349
|
+
if @connection.is_closed
|
350
|
+
reconnect!
|
351
|
+
retry
|
352
|
+
else
|
353
|
+
raise
|
354
|
+
end
|
222
355
|
ensure
|
223
356
|
stmt.close
|
224
357
|
end
|
@@ -240,7 +373,7 @@ module ActiveRecord
|
|
240
373
|
end
|
241
374
|
|
242
375
|
private
|
243
|
-
def unmarshal_result(resultset)
|
376
|
+
def unmarshal_result(resultset, &row_filter)
|
244
377
|
metadata = resultset.getMetaData
|
245
378
|
column_count = metadata.getColumnCount
|
246
379
|
column_names = ['']
|
@@ -252,15 +385,22 @@ module ActiveRecord
|
|
252
385
|
column_types << metadata.getColumnType(i)
|
253
386
|
column_scale << metadata.getScale(i)
|
254
387
|
end
|
255
|
-
|
388
|
+
|
256
389
|
results = []
|
257
390
|
|
391
|
+
# take all rows if block not supplied
|
392
|
+
row_filter = lambda{|result_row| true} unless block_given?
|
393
|
+
|
258
394
|
while resultset.next
|
259
|
-
row
|
260
|
-
|
261
|
-
|
395
|
+
# let the supplied block look at this row from the resultset to
|
396
|
+
# see if we want to include it in our results
|
397
|
+
if row_filter.call(resultset)
|
398
|
+
row = {}
|
399
|
+
1.upto(column_count) do |i|
|
400
|
+
row[column_names[i].downcase] = convert_jdbc_type_to_ruby(i, column_types[i], column_scale[i], resultset)
|
401
|
+
end
|
402
|
+
results << row
|
262
403
|
end
|
263
|
-
results << row
|
264
404
|
end
|
265
405
|
|
266
406
|
results
|
@@ -289,71 +429,98 @@ module ActiveRecord
|
|
289
429
|
|
290
430
|
results
|
291
431
|
end
|
292
|
-
|
293
|
-
def to_ruby_time(
|
294
|
-
if
|
295
|
-
tm =
|
432
|
+
|
433
|
+
def to_ruby_time(java_time)
|
434
|
+
if java_time
|
435
|
+
tm = java_time.getTime
|
296
436
|
Time.at(tm / 1000, (tm % 1000) * 1000)
|
297
437
|
end
|
298
438
|
end
|
299
439
|
|
440
|
+
def to_ruby_date(java_date)
|
441
|
+
if java_date
|
442
|
+
cal = java.util.Calendar.getInstance
|
443
|
+
cal.setTime(java_date)
|
444
|
+
Date.new(cal.get(java.util.Calendar::YEAR), cal.get(java.util.Calendar::MONTH)+1, cal.get(java.util.Calendar::DATE))
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
300
448
|
def convert_jdbc_type_to_ruby(row, type, scale, resultset)
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
resultset.getString(row)
|
308
|
-
when Jdbc::Types::SMALLINT, Jdbc::Types::INTEGER, Jdbc::Types::NUMERIC, Jdbc::Types::BIGINT
|
309
|
-
resultset.getInt(row)
|
310
|
-
when Jdbc::Types::BIT, Jdbc::Types::BOOLEAN, Jdbc::Types::TINYINT, Jdbc::Types::DECIMAL
|
311
|
-
resultset.getBoolean(row)
|
312
|
-
when Jdbc::Types::FLOAT, Jdbc::Types::DOUBLE
|
313
|
-
resultset.getDouble(row)
|
314
|
-
when Jdbc::Types::TIMESTAMP
|
315
|
-
to_ruby_time(resultset.getTimestamp(row))
|
316
|
-
when Jdbc::Types::TIME
|
317
|
-
to_ruby_time(resultset.getTime(row))
|
318
|
-
when Jdbc::Types::DATE
|
319
|
-
to_ruby_time(resultset.getDate(row))
|
320
|
-
when Jdbc::Types::LONGVARBINARY, Jdbc::Types::BLOB, Jdbc::Types::BINARY, Jdbc::Types::VARBINARY
|
321
|
-
resultset.getString(row)
|
449
|
+
value = case type
|
450
|
+
when Jdbc::Types::CHAR, Jdbc::Types::VARCHAR, Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB
|
451
|
+
resultset.getString(row)
|
452
|
+
when Jdbc::Types::NUMERIC, Jdbc::Types::BIGINT
|
453
|
+
if scale != 0
|
454
|
+
BigDecimal.new(resultset.getBigDecimal(row).toString)
|
322
455
|
else
|
323
|
-
|
324
|
-
name = types.find {|t| Jdbc::Types.const_get(t.to_sym) == type}
|
325
|
-
raise "jdbc_adapter: type #{name} not supported yet"
|
456
|
+
resultset.getLong(row)
|
326
457
|
end
|
458
|
+
when Jdbc::Types::DECIMAL
|
459
|
+
BigDecimal.new(resultset.getBigDecimal(row).toString)
|
460
|
+
when Jdbc::Types::SMALLINT, Jdbc::Types::INTEGER
|
461
|
+
resultset.getInt(row)
|
462
|
+
when Jdbc::Types::BIT, Jdbc::Types::BOOLEAN, Jdbc::Types::TINYINT
|
463
|
+
resultset.getBoolean(row)
|
464
|
+
when Jdbc::Types::FLOAT, Jdbc::Types::DOUBLE
|
465
|
+
resultset.getDouble(row)
|
466
|
+
when Jdbc::Types::TIMESTAMP
|
467
|
+
# FIXME: This should not be a catchall and it should move this to mysql since it
|
468
|
+
# is catching non-existent date 0000-00:00:00
|
469
|
+
begin
|
470
|
+
to_ruby_time(resultset.getTimestamp(row))
|
471
|
+
rescue java.sql.SQLException
|
472
|
+
nil
|
473
|
+
end
|
474
|
+
when Jdbc::Types::TIME
|
475
|
+
to_ruby_time(resultset.getTime(row))
|
476
|
+
when Jdbc::Types::DATE
|
477
|
+
to_ruby_date(resultset.getDate(row))
|
478
|
+
when Jdbc::Types::LONGVARBINARY, Jdbc::Types::BLOB, Jdbc::Types::BINARY, Jdbc::Types::VARBINARY
|
479
|
+
resultset.getString(row)
|
480
|
+
else
|
481
|
+
raise "jdbc_adapter: type #{jdbc_type_name(type)} not supported yet"
|
327
482
|
end
|
483
|
+
resultset.wasNull ? nil : value
|
484
|
+
end
|
485
|
+
def jdbc_type_name(type)
|
486
|
+
Jdbc::Types.constants.find {|t| Jdbc::Types.const_get(t.to_sym) == type}
|
328
487
|
end
|
329
488
|
end
|
330
489
|
|
331
490
|
class JdbcAdapter < AbstractAdapter
|
491
|
+
ADAPTER_TYPES = {
|
492
|
+
/oracle/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::Oracle)},
|
493
|
+
/mimer/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::Mimer)},
|
494
|
+
/postgre/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::PostgreSQL)},
|
495
|
+
/mysql/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::MySQL)},
|
496
|
+
/sqlserver|tds/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::MsSQL)},
|
497
|
+
/hsqldb|\.h2\./i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::HSQLDB)},
|
498
|
+
/derby/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::Derby)},
|
499
|
+
/db2/i => lambda{|cfg,adapt|
|
500
|
+
if cfg[:url] =~ /^jdbc:derby:net:/
|
501
|
+
adapt.extend(JdbcSpec::Derby)
|
502
|
+
else
|
503
|
+
adapt.extend(JdbcSpec::DB2)
|
504
|
+
end},
|
505
|
+
/firebird/i => lambda{|cfg,adapt| adapt.extend(JdbcSpec::FireBird)}
|
506
|
+
|
507
|
+
}
|
508
|
+
|
332
509
|
def initialize(connection, logger, config)
|
333
510
|
super(connection, logger)
|
334
511
|
@config = config
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
when /sqlserver|tds/i: self.extend(JdbcSpec::MsSQL)
|
341
|
-
when /hsqldb/i: self.extend(JdbcSpec::HSQLDB)
|
342
|
-
when /derby/i: self.extend(JdbcSpec::Derby)
|
343
|
-
when /db2/i:
|
344
|
-
if config[:url] =~ /^jdbc:derby:net:/
|
345
|
-
self.extend(JdbcSpec::Derby)
|
346
|
-
else
|
347
|
-
self.extend(JdbcSpec::DB2)
|
348
|
-
end
|
349
|
-
when /firebird/i: self.extend(JdbcSpec::FireBird)
|
512
|
+
ds = config[:driver].to_s
|
513
|
+
for reg, func in ADAPTER_TYPES
|
514
|
+
if reg === ds
|
515
|
+
func.call(@config,self)
|
516
|
+
end
|
350
517
|
end
|
351
518
|
end
|
352
519
|
|
353
520
|
def modify_types(tp)
|
354
521
|
tp
|
355
522
|
end
|
356
|
-
|
523
|
+
|
357
524
|
def adapter_name #:nodoc:
|
358
525
|
'JDBC'
|
359
526
|
end
|
@@ -400,7 +567,7 @@ module ActiveRecord
|
|
400
567
|
end
|
401
568
|
return nil,nil
|
402
569
|
end
|
403
|
-
|
570
|
+
|
404
571
|
def active?
|
405
572
|
true
|
406
573
|
end
|
@@ -439,7 +606,7 @@ module ActiveRecord
|
|
439
606
|
end
|
440
607
|
|
441
608
|
def columns(table_name, name = nil)
|
442
|
-
@connection.columns(table_name)
|
609
|
+
@connection.columns(table_name.to_s)
|
443
610
|
end
|
444
611
|
|
445
612
|
def tables
|