ActiveRecord-JDBC 0.2.2 → 0.2.3
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/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
|