dbd-jdbc 0.0.4 → 0.0.5
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/README.txt +64 -12
- data/Rakefile +2 -1
- data/lib/dbd/Jdbc.rb +51 -362
- data/lib/dbd/jdbc/database.rb +178 -0
- data/lib/dbd/jdbc/driver.rb +70 -0
- data/lib/dbd/jdbc/statement.rb +199 -0
- data/lib/dbd/{JdbcTypeConversions.rb → jdbc/type_conversion.rb} +20 -21
- metadata +12 -7
data/README.txt
CHANGED
@@ -1,33 +1,85 @@
|
|
1
1
|
RECENT CHANGES
|
2
2
|
==============
|
3
|
-
The dbd-jdbc project has recently moved to Kenai and can be found at the following url
|
3
|
+
The dbd-jdbc project has recently moved to Kenai and can be found at the following url
|
4
|
+
(http://kenai.com/projects/dbd-jdbc). Please report any bugs found to the issue
|
5
|
+
tracker listed on the Kenai project page.
|
4
6
|
|
5
7
|
OVERVIEW
|
6
8
|
========
|
7
|
-
The Jdbc driver for DBI runs only under JRuby, using JRuby's Java integration to act
|
9
|
+
The Jdbc driver for DBI runs only under JRuby, using JRuby's Java integration to act
|
10
|
+
as a thin wrapper around JDBC Connections obtained through DriverManager.
|
11
|
+
Theoretically this driver can support any database that has JDBC support, although the
|
12
|
+
behavior of certain aspects of the DBI API will differ slightly based on the
|
13
|
+
implementation of the JDBC driver and the underlying database (see LIMITATIONS)
|
8
14
|
|
9
15
|
INSTALLATION
|
10
16
|
============
|
11
|
-
Besides the ruby files being installed with DBI (in DBD/Jdbc), the JDBC driver classes
|
17
|
+
Besides the ruby files being installed with DBI (in DBD/Jdbc), the JDBC driver classes
|
18
|
+
must be added to the JRuby classpath. One way to do this on UNIX machines is to
|
19
|
+
place a JDBC driver jarfile in JRUBY_HOME/lib. Another way is to set the CLASSPATH
|
20
|
+
environment variable. The jruby (or jruby.bat) script can also be modified to add to
|
21
|
+
the classpath. In the future there may be more dynamic ways to add jarfiles to the
|
22
|
+
classpath as well; check with the JRuby documentation.
|
12
23
|
|
13
24
|
USAGE
|
14
25
|
=====
|
15
|
-
This driver is used like any standard DBI driver, by requiring 'dbi' and obtaining a
|
26
|
+
This driver is used like any standard DBI driver, by requiring 'dbi' and obtaining a
|
27
|
+
connection through the DBI.connect method. The DSN for the database should be "dbi:"
|
28
|
+
followed by the standard Java database URL (jdbc:<subprotocol>:<subname>). In
|
29
|
+
addition, the attributes hash passed to the connect method must contain the key/value
|
30
|
+
pair "driver" => <jdbc driver class name>. For example (MySQL):
|
16
31
|
|
17
|
-
dbh = DBI.connect('dbi:jdbc:mysql://localhost:3306/test', 'anonymous', '',
|
32
|
+
dbh = DBI.connect('dbi:jdbc:mysql://localhost:3306/test', 'anonymous', '',
|
33
|
+
{ "driver" => "com.mysql.jdbc.Driver" } )
|
18
34
|
|
19
35
|
SUPPORTED ATTRIBUTES
|
20
36
|
====================
|
21
|
-
Besides the mandatory "driver" attribute that must be passed in the DBI connect method,
|
22
|
-
|
23
|
-
|
37
|
+
Besides the mandatory "driver" attribute that must be passed in the DBI connect method,
|
38
|
+
there are additional attributes that can be passed to modify the behavior of the
|
39
|
+
driver. In addition to setting these attributes during the connect call, they can be
|
40
|
+
set on a driver handle using []= (e.g. dbh["autocommit"] = true). The currently
|
41
|
+
supported attributes are:
|
42
|
+
|
43
|
+
1) autocommit: sets the autoCommit status of the underlying JDBC Connection
|
44
|
+
(there is a 1-1 relationship between a DBI Database instance and a JDBC Connection)
|
45
|
+
2) nulltype: sets the java.sql.Types constant to use when binding nil to a Statement.
|
46
|
+
See LIMITATIONS
|
24
47
|
|
25
48
|
LIMITATIONS
|
26
49
|
===========
|
27
|
-
There are limitations to the JDBC driver that are largely based on incompatibilities
|
50
|
+
There are limitations to the JDBC driver that are largely based on incompatibilities
|
51
|
+
between the DBI and JDBC APIs, and the differences in underlying JDBC driver
|
52
|
+
implementations.
|
28
53
|
|
29
|
-
1) Binding nil to statements is somewhat unreliable due to the fact that JDBC requires
|
54
|
+
1) Binding nil to statements is somewhat unreliable due to the fact that JDBC requires
|
55
|
+
type information in PreparedStatement.setNull(int), but there is no type information
|
56
|
+
associated with nil in ruby. E.g., statements like DBI.select_all("select * from a, b,
|
57
|
+
c where col1=? and col2=?", "a", nil) might run into problems. One workaround is to
|
58
|
+
hardcode NULL in the sql statement. If executing multiple inserts and some values
|
59
|
+
might be nil, the driver will call setNull with the type specified in the driver
|
60
|
+
attribute "nulltype". If the default fails with a given driver, try specifying
|
61
|
+
different nulltypes such as "dbh['nulltype'] = java.sql.Types::NULL" or
|
62
|
+
"dbh['nulltype'] = java.sql.Types::OTHER" to see if it will work.
|
30
63
|
|
31
|
-
2) Type conversion in result sets: Java to Ruby type conversion in results of queries
|
64
|
+
2) Type conversion in result sets: Java to Ruby type conversion in results of queries
|
65
|
+
is more limited than in JDBC, since DBI returns an entire row at a time, without type
|
66
|
+
information specified (unlike JDBC ResultSet where getInt, getString, etc allow each
|
67
|
+
column to be typed appropriately). The driver attempts to convert each data type,
|
68
|
+
relying mostly on getObject() and the JRuby type conversion system. Due to the fact
|
69
|
+
that JDBC drivers are not constrained to the exact Java types returned for each SQL
|
70
|
+
type, it is possible for some conversion oddities to arise (e.g. returning Java
|
71
|
+
BigDecimal which isn't converted by JRuby, leading to a Java BigDecimal rather than
|
72
|
+
Ruby Fixnum/BigDecimal).
|
32
73
|
|
33
|
-
3) Type conversion in prepared statements: In addition to not being able to use type
|
74
|
+
3) Type conversion in prepared statements: In addition to not being able to use type
|
75
|
+
data when returning data, it isn't possible to specify type data directly when binding
|
76
|
+
parameters either (when binding multiple parameters, DBI has ability to set type data,
|
77
|
+
and when calling bind_param, this driver does not currently support type data in the
|
78
|
+
attributes). Most conversions should work without problem, but there is ambiguity in
|
79
|
+
the Ruby Float type since it can be treated as float or double (or BigDecimal) in Java
|
80
|
+
(and FLOAT, DOUBLE, REAL, NUMERIC, DECIMAL, etc in java.sql.Types). setDouble() is
|
81
|
+
used to try to retain the highest precision, but some problems have been seen (e.g.
|
82
|
+
Sybase REAL works better with setFloat()). When doing inserts or retrieval, be sure
|
83
|
+
that the driver keeps the desired precision (the only workaround is to change the
|
84
|
+
database column type to something that works better for doubles with the given JDBC
|
85
|
+
driver and database)
|
data/Rakefile
CHANGED
@@ -28,11 +28,12 @@ Rake::Task['manifest'].invoke # Always regen manifest, so Hoe has up-to-date lis
|
|
28
28
|
|
29
29
|
begin
|
30
30
|
require 'hoe'
|
31
|
-
Hoe.new("dbd-jdbc", "0.0.
|
31
|
+
Hoe.new("dbd-jdbc", "0.0.5") do |p|
|
32
32
|
p.rubyforge_name = "jruby-extras"
|
33
33
|
p.url = "http://kenai.com/projects/dbd-jdbc"
|
34
34
|
p.author = "Chad Johnson"
|
35
35
|
p.email = "chad.j.johnson@gmail.com"
|
36
|
+
p.description = "A JDBC DBD driver for Ruby DBI"
|
36
37
|
p.summary = "JDBC driver for DBI, originally by Kristopher Schmidt and Ola Bini"
|
37
38
|
end.spec.dependencies.delete_if { |dep| dep.name == "hoe" }
|
38
39
|
rescue LoadError
|
data/lib/dbd/Jdbc.rb
CHANGED
@@ -27,16 +27,54 @@
|
|
27
27
|
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
|
29
29
|
require 'java'
|
30
|
-
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'rubygems'
|
33
|
+
gem 'dbi'
|
34
|
+
rescue LoadError => e
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'dbi'
|
31
38
|
|
32
39
|
module DBI
|
33
40
|
module DBD
|
41
|
+
#
|
42
|
+
# DBD::Jdbc - Database Driver for Java / JDBC
|
43
|
+
#
|
44
|
+
# Requires DBI and JRuby
|
45
|
+
#
|
34
46
|
module Jdbc
|
35
|
-
|
36
47
|
include_class 'java.sql.Connection'
|
37
48
|
include_class 'java.sql.ResultSet'
|
49
|
+
include_class 'java.util.HashMap'
|
50
|
+
include_class 'java.util.Collections'
|
51
|
+
|
52
|
+
VERSION = "0.0.5"
|
53
|
+
DESCRIPTION = "JDBC DBD driver for JRuby"
|
38
54
|
|
39
|
-
|
55
|
+
#
|
56
|
+
# Transaction isolation levels copied from JDBC
|
57
|
+
#
|
58
|
+
TRANSACTION_NONE = Connection::TRANSACTION_NONE
|
59
|
+
TRANSACTION_READ_COMMITTED = Connection::TRANSACTION_READ_COMMITTED
|
60
|
+
TRANSACTION_READ_UNCOMMITTED = Connection::TRANSACTION_READ_UNCOMMITTED
|
61
|
+
TRANSACTION_REPEATABLE_READ = Connection::TRANSACTION_REPEATABLE_READ
|
62
|
+
TRANSACTION_SERIALIZABLE = Connection::TRANSACTION_SERIALIZABLE
|
63
|
+
# Convience isolation levels
|
64
|
+
NONE = Connection::TRANSACTION_NONE
|
65
|
+
READ_COMMITTED = Connection::TRANSACTION_READ_COMMITTED
|
66
|
+
READ_UNCOMMITTED = Connection::TRANSACTION_READ_UNCOMMITTED
|
67
|
+
REPEATABLE_READ = Connection::TRANSACTION_REPEATABLE_READ
|
68
|
+
SERIALIZABLE = Connection::TRANSACTION_SERIALIZABLE
|
69
|
+
|
70
|
+
#
|
71
|
+
# returns 'Jdbc'
|
72
|
+
#
|
73
|
+
# See DBI::TypeUtil#convert for more information.
|
74
|
+
#
|
75
|
+
def self.driver_name
|
76
|
+
"Jdbc"
|
77
|
+
end
|
40
78
|
|
41
79
|
module Type
|
42
80
|
class Timestamp < DBI::Type::Null
|
@@ -56,369 +94,20 @@ module DBI
|
|
56
94
|
end
|
57
95
|
end
|
58
96
|
end
|
59
|
-
|
60
|
-
def self.driver_name
|
61
|
-
"Jdbc"
|
62
|
-
end
|
63
|
-
|
97
|
+
|
64
98
|
#
|
65
99
|
# Bound values pass through this function before being handed to Statement.bind_param
|
66
|
-
#
|
100
|
+
#
|
67
101
|
# The false value prevents the default type conversion from occurring
|
68
102
|
#
|
69
103
|
DBI::TypeUtil.register_conversion(driver_name) do |obj|
|
70
104
|
[obj, false]
|
71
105
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
@driverAttributes = [ "driver" ]
|
81
|
-
end
|
82
|
-
|
83
|
-
# Stolen from AR-JDBC
|
84
|
-
def driver_class(name)
|
85
|
-
return @driver_class if @driver_class
|
86
|
-
|
87
|
-
driver_class_const = (name[0...1].capitalize + name[1..name.length]).gsub(/\./, '_')
|
88
|
-
driver_class_name = name
|
89
|
-
Driver.module_eval do
|
90
|
-
include_class(driver_class_name) { driver_class_const }
|
91
|
-
end
|
92
|
-
driver_class = Driver.const_get(driver_class_const)
|
93
|
-
raise "You specify a driver for your JDBC connection" unless driver_class
|
94
|
-
@driver_class = driver_class
|
95
|
-
end
|
96
|
-
|
97
|
-
def load(name)
|
98
|
-
java.sql.DriverManager.registerDriver(driver_class(name).new)
|
99
|
-
end
|
100
|
-
|
101
|
-
def connect(dbname, user, auth, attr)
|
102
|
-
driverClass = attr["driver"]
|
103
|
-
raise InterfaceError.new('driver class name must be specified as "driver" in connection attributes') unless driverClass
|
104
|
-
load(driverClass)
|
105
|
-
connection = java.sql.DriverManager.getConnection("jdbc:"+dbname, user, auth)
|
106
|
-
dbh = Database.new(connection)
|
107
|
-
(attr.keys - @driverAttributes).each { |key| dbh[key] = attr[key] }
|
108
|
-
return dbh
|
109
|
-
rescue NativeException => error
|
110
|
-
raise DBI::DatabaseError.new(error.message)
|
111
|
-
end
|
112
|
-
|
113
|
-
end #Driver
|
114
|
-
|
115
|
-
class Database < DBI::BaseDatabase
|
116
|
-
|
117
|
-
include JdbcTypeConversions
|
118
|
-
|
119
|
-
def initialize(connection)
|
120
|
-
@connection = connection
|
121
|
-
@attributes = {
|
122
|
-
#works with Sybase and Mysql.
|
123
|
-
"nulltype" => java.sql.Types::VARCHAR,
|
124
|
-
"allow_scroll" => false
|
125
|
-
}
|
126
|
-
end
|
127
|
-
|
128
|
-
def disconnect
|
129
|
-
@connection.rollback unless self["autocommit"]
|
130
|
-
@connection.close
|
131
|
-
rescue NativeException => error
|
132
|
-
raise DBI::DatabaseError.new(error.message)
|
133
|
-
end
|
134
|
-
|
135
|
-
def prepare(sql)
|
136
|
-
if self["allow_scroll"]
|
137
|
-
return Statement.new(@connection.prepareStatement(sql,ResultSet::TYPE_SCROLL_INSENSITIVE, ResultSet::CONCUR_READ_ONLY), self["nulltype"], self["allow_scroll"])
|
138
|
-
else
|
139
|
-
return Statement.new(@connection.prepareStatement(sql), self["nulltype"], self["allow_scroll"])
|
140
|
-
end
|
141
|
-
rescue NativeException => error
|
142
|
-
raise DBI::DatabaseError.new(error.message)
|
143
|
-
end
|
144
|
-
|
145
|
-
def do(statement, *bindvars)
|
146
|
-
res = nil
|
147
|
-
if bindvars.nil? || bindvars.empty?
|
148
|
-
stmt = @connection.createStatement()
|
149
|
-
begin
|
150
|
-
stmt.execute(statement)
|
151
|
-
ensure
|
152
|
-
stmt.close rescue NativeException
|
153
|
-
end
|
154
|
-
else
|
155
|
-
stmt = execute(statement, *bindvars)
|
156
|
-
res = stmt.rows
|
157
|
-
stmt.finish
|
158
|
-
end
|
159
|
-
return res
|
160
|
-
rescue NativeException => error
|
161
|
-
raise DBI::DatabaseError.new(error.message)
|
162
|
-
end
|
163
|
-
|
164
|
-
def ping
|
165
|
-
return !@connection.isClosed
|
166
|
-
rescue NativeException => error
|
167
|
-
raise DBI::DatabaseError.new(error.message)
|
168
|
-
end
|
169
|
-
|
170
|
-
def commit
|
171
|
-
@connection.commit()
|
172
|
-
rescue NativeException => error
|
173
|
-
raise DBI::DatabaseError.new(error.message)
|
174
|
-
end
|
175
|
-
|
176
|
-
def rollback
|
177
|
-
@connection.rollback()
|
178
|
-
rescue NativeException => error
|
179
|
-
raise DBI::DatabaseError.new(error.message)
|
180
|
-
end
|
181
|
-
|
182
|
-
def tables
|
183
|
-
rs = @connection.getMetaData.getTables(nil, nil, nil, nil)
|
184
|
-
tables = []
|
185
|
-
while(rs.next())
|
186
|
-
tables << rs.getString(3)
|
187
|
-
end
|
188
|
-
return tables
|
189
|
-
rescue NativeException => error
|
190
|
-
raise DBI::DatabaseError.new(error.message)
|
191
|
-
end
|
192
|
-
|
193
|
-
def columns(table)
|
194
|
-
metaData = @connection.getMetaData()
|
195
|
-
rs = metaData.getColumns(nil, nil, table, nil)
|
196
|
-
columns = []
|
197
|
-
while(rs.next())
|
198
|
-
type_name, dbi_type = jdbc_to_dbi_sqltype(rs.getShort(5))
|
199
|
-
columns << {
|
200
|
-
"name" => rs.getString(4),
|
201
|
-
"sql_type" => type_name,
|
202
|
-
"type_name" => rs.getString(6),
|
203
|
-
"precision" => rs.getInt(7),
|
204
|
-
"scale" => rs.getInt(9),
|
205
|
-
"default" => rs.getString(13),
|
206
|
-
"nullable" => (rs.getInt(11) == 1)
|
207
|
-
}
|
208
|
-
columns[-1]["dbi_type"] = dbi_type if dbi_type
|
209
|
-
end
|
210
|
-
return columns
|
211
|
-
rescue NativeException => error
|
212
|
-
raise DBI::DatabaseError.new(error.message)
|
213
|
-
end
|
214
|
-
|
215
|
-
def [](attribute)
|
216
|
-
attribute = attribute.downcase
|
217
|
-
check_attribute(attribute)
|
218
|
-
case attribute
|
219
|
-
when "autocommit" then @connection.getAutoCommit()
|
220
|
-
else
|
221
|
-
@attributes[attribute]
|
222
|
-
end
|
223
|
-
rescue NativeException => error
|
224
|
-
raise DBI::DatabaseError.new(error.message)
|
225
|
-
end
|
226
|
-
|
227
|
-
def []=(attribute, value)
|
228
|
-
attribute = attribute.downcase
|
229
|
-
check_attribute(attribute)
|
230
|
-
case attribute
|
231
|
-
when "autocommit" then @connection.setAutoCommit(value)
|
232
|
-
else
|
233
|
-
@attributes[attribute] = value
|
234
|
-
end
|
235
|
-
rescue NativeException => error
|
236
|
-
raise DBI::DatabaseError.new(error.message)
|
237
|
-
end
|
238
|
-
|
239
|
-
def __get_java_connection
|
240
|
-
return @connection
|
241
|
-
end
|
242
|
-
|
243
|
-
def self.from_java_connection(java_connection, type_coercion = false)
|
244
|
-
DBI::DatabaseHandle.new(Database.new(java_connection), type_coercion)
|
245
|
-
end
|
246
|
-
|
247
|
-
private
|
248
|
-
|
249
|
-
def check_attribute(attribute)
|
250
|
-
raise DBI::NotSupportedError.new("Database attribute #{attribute} is not supported") unless (attribute == "autocommit" || @attributes.has_key?(attribute))
|
251
|
-
end
|
252
|
-
|
253
|
-
end
|
254
|
-
|
255
|
-
class Statement < DBI::BaseStatement
|
256
|
-
|
257
|
-
include JdbcTypeConversions
|
258
|
-
|
259
|
-
def initialize(statement, nulltype, allow_scroll = false)
|
260
|
-
@statement = statement
|
261
|
-
@nulltype = nulltype
|
262
|
-
@allow_scroll = allow_scroll
|
263
|
-
@rows = nil
|
264
|
-
@data = []
|
265
|
-
end
|
266
|
-
|
267
|
-
def bind_param(param, value, attribs)
|
268
|
-
raise InterfaceError.new("Statement.bind_param only supports numeric placeholder numbers") unless param.is_a?(Fixnum)
|
269
|
-
if value.nil?
|
270
|
-
@statement.setNull(param, @nulltype)
|
271
|
-
elsif value.is_a?(String)
|
272
|
-
@statement.setString(param, value)
|
273
|
-
elsif value.is_a?(Fixnum)
|
274
|
-
#no reason not to coerce it to a long?
|
275
|
-
@statement.setLong(param, value)
|
276
|
-
elsif value.is_a?(Float)
|
277
|
-
#despite DBD spec saying Float->SQL Float, using Double gives
|
278
|
-
#better precision and passes tests that setFloat does not.
|
279
|
-
@statement.setDouble(param, value)
|
280
|
-
elsif value.is_a?(::DateTime) || value.is_a?(DBI::Timestamp)
|
281
|
-
@statement.setTimestamp(param, timestamp_to_jdbctimestamp(value))
|
282
|
-
elsif value.is_a?(::Date) || value.is_a?(DBI::Date)
|
283
|
-
@statement.setDate(param, date_to_jdbcdate(value))
|
284
|
-
elsif value.is_a?(::Time) || value.is_a?(DBI::Time)
|
285
|
-
@statement.setTime(param, time_to_jdbctime(value))
|
286
|
-
else
|
287
|
-
@statement.setObject(param, value)
|
288
|
-
end
|
289
|
-
rescue NativeException => error
|
290
|
-
raise DBI::DatabaseError.new(error.message)
|
291
|
-
end
|
292
|
-
|
293
|
-
def execute
|
294
|
-
if @statement.execute()
|
295
|
-
@rs = @statement.getResultSet
|
296
|
-
@rows = nil
|
297
|
-
@data.clear
|
298
|
-
else
|
299
|
-
@rs = nil
|
300
|
-
@rows = @statement.getUpdateCount
|
301
|
-
end
|
302
|
-
rescue NativeException => error
|
303
|
-
raise DBI::DatabaseError.new(error.message)
|
304
|
-
end
|
305
|
-
|
306
|
-
def finish
|
307
|
-
@statement.close()
|
308
|
-
@rs = nil
|
309
|
-
@rows = nil
|
310
|
-
rescue NativeException => error
|
311
|
-
raise DBI::DatabaseError.new(error.message)
|
312
|
-
end
|
313
|
-
|
314
|
-
def fetch
|
315
|
-
if (@rs && @rs.next())
|
316
|
-
return fill_data
|
317
|
-
else
|
318
|
-
return nil
|
319
|
-
end
|
320
|
-
rescue NativeException => error
|
321
|
-
raise DBI::DatabaseError.new(error.message)
|
322
|
-
end
|
323
|
-
|
324
|
-
def fill_data
|
325
|
-
@data.clear
|
326
|
-
metaData = @rs.getMetaData()
|
327
|
-
(1..metaData.getColumnCount()).each do |columnNumber|
|
328
|
-
@data << get_value(columnNumber, @rs, metaData)
|
329
|
-
end
|
330
|
-
return @data
|
331
|
-
end
|
332
|
-
|
333
|
-
#
|
334
|
-
# Unless "allow_scroll" was set on this connection this will default to the
|
335
|
-
# DBI::BaseStatement#fetch_scroll implementation.
|
336
|
-
#
|
337
|
-
# See DBI::BaseStatement#fetch_scroll. These additional constants are supported
|
338
|
-
# when "allow_scroll" is set on the connection.
|
339
|
-
#
|
340
|
-
# * DBI::SQL_FETCH_PRIOR: Fetch the row previous to the current one.
|
341
|
-
# * DBI::SQL_FETCH_FIRST: Fetch the first row.
|
342
|
-
# * DBI::SQL_FETCH_ABSOLUTE: Fetch the row at the offset provided.
|
343
|
-
# * DBI::SQL_FETCH_RELATIVE: Fetch the row at the current point + offset.
|
344
|
-
#
|
345
|
-
def fetch_scroll(direction, offset)
|
346
|
-
return super(direction, offset) unless @allow_scroll
|
347
|
-
|
348
|
-
case direction
|
349
|
-
when DBI::SQL_FETCH_NEXT
|
350
|
-
fill_data if @rs && @rs.next()
|
351
|
-
when DBI::SQL_FETCH_PRIOR
|
352
|
-
fill_data if @rs && @rs.previous()
|
353
|
-
when DBI::SQL_FETCH_FIRST
|
354
|
-
fill_data if @rs && @rs.first()
|
355
|
-
when DBI::SQL_FETCH_LAST
|
356
|
-
fill_data if @rs && @rs.last()
|
357
|
-
when DBI::SQL_FETCH_ABSOLUTE
|
358
|
-
fill_data if @rs && @rs.absolute(offset)
|
359
|
-
when DBI::SQL_FETCH_RELATIVE
|
360
|
-
fill_data if @rs && @rs.relative(offset)
|
361
|
-
else
|
362
|
-
raise DBI::NotSupportedError
|
363
|
-
end
|
364
|
-
rescue NativeException => error
|
365
|
-
raise DBI::NotSupportedError.new(error.message)
|
366
|
-
end
|
367
|
-
|
368
|
-
def column_info
|
369
|
-
info = Array.new
|
370
|
-
return info unless @rs
|
371
|
-
metaData = @rs.getMetaData()
|
372
|
-
(1..metaData.getColumnCount()).each do |columnNumber|
|
373
|
-
type_name, dbi_type = jdbc_to_dbi_sqltype(metaData.getColumnType(columnNumber))
|
374
|
-
info << {
|
375
|
-
"name" => metaData.getColumnName(columnNumber),
|
376
|
-
"sql_type" => type_name,
|
377
|
-
"type_name" => metaData.getColumnTypeName(columnNumber),
|
378
|
-
"precision" => metaData.getPrecision(columnNumber),
|
379
|
-
"scale" => metaData.getScale(columnNumber),
|
380
|
-
"nullable" => (metaData.isNullable(columnNumber) == 1)
|
381
|
-
}
|
382
|
-
info[-1]["dbi_type"] = dbi_type if dbi_type
|
383
|
-
end
|
384
|
-
return info
|
385
|
-
rescue NativeException => error
|
386
|
-
raise DBI::DatabaseError.new(error.message)
|
387
|
-
end
|
388
|
-
|
389
|
-
def rows
|
390
|
-
return @rows
|
391
|
-
end
|
392
|
-
|
393
|
-
def self.from_java_statement(java_statement, type_coercion = false, null_type = java.sql.Types::VARCHAR)
|
394
|
-
raise DBI::DatabaseError.new("Only java.sql.PreparedStatement instances accepted") unless java_statement.kind_of?(java.sql.PreparedStatement)
|
395
|
-
|
396
|
-
DBI::StatementHandle.new(Statement.new(java_statement, null_type), true, true, type_coercion)
|
397
|
-
end
|
398
|
-
|
399
|
-
private
|
400
|
-
|
401
|
-
def get_value(columnNumber, rs, metaData)
|
402
|
-
#note only map things that seem unlikely to coerce properly to jruby,
|
403
|
-
#since anything mapped as primitive type cannot be returned as null
|
404
|
-
return case metaData.getColumnType(columnNumber)
|
405
|
-
when java.sql.Types::BIT
|
406
|
-
rs.getBoolean(columnNumber)
|
407
|
-
when java.sql.Types::NUMERIC, java.sql.Types::DECIMAL
|
408
|
-
metaData.getScale(columnNumber) == 0 ? rs.getLong(columnNumber) : rs.getDouble(columnNumber)
|
409
|
-
when java.sql.Types::DATE
|
410
|
-
jdbcdate_to_date(rs.getDate(columnNumber))
|
411
|
-
when java.sql.Types::TIME
|
412
|
-
jdbctime_to_time(rs.getTime(columnNumber))
|
413
|
-
when java.sql.Types::TIMESTAMP
|
414
|
-
jdbctimestamp_to_timestamp(rs.getTimestamp(columnNumber))
|
415
|
-
else
|
416
|
-
rs.getObject(columnNumber)
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
end # class Statement
|
421
|
-
|
422
|
-
end # module Jdbc
|
423
|
-
end # module DBD
|
424
|
-
end # module DBI
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
require 'dbd/jdbc/type_conversion'
|
111
|
+
require 'dbd/jdbc/driver'
|
112
|
+
require 'dbd/jdbc/database'
|
113
|
+
require 'dbd/jdbc/statement'
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# (C) 2009 Chad Johnson <chad.j.johnson@gmail.com>.
|
2
|
+
# (C) 2008 Ola Bini <ola.bini@gmail.com>.
|
3
|
+
# (C) 2006 Kristopher Schmidt <krisschmidt@optonline.net>.
|
4
|
+
#
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
# Redistribution and use in source and binary forms, with or without
|
8
|
+
# modification, are permitted provided that the following conditions
|
9
|
+
# are met:
|
10
|
+
# 1. Redistributions of source code must retain the above copyright
|
11
|
+
# notice, this list of conditions and the following disclaimer.
|
12
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
# 3. The name of the author may not be used to endorse or promote products
|
16
|
+
# derived from this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
19
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
20
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
21
|
+
# THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
22
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
23
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
24
|
+
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
26
|
+
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
27
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
module DBI::DBD::Jdbc
|
30
|
+
#
|
31
|
+
# Models the DBI::BaseDatabase API to create DBI::DatabaseHandle objects.
|
32
|
+
#
|
33
|
+
class Database < DBI::BaseDatabase
|
34
|
+
include TypeConversions
|
35
|
+
|
36
|
+
def initialize(connection)
|
37
|
+
@connection = connection
|
38
|
+
@attributes = {
|
39
|
+
#works with Sybase and Mysql.
|
40
|
+
"nulltype" => java.sql.Types::VARCHAR,
|
41
|
+
"allow_scroll" => false
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def disconnect
|
46
|
+
@connection.rollback unless self["autocommit"]
|
47
|
+
@connection.close
|
48
|
+
rescue NativeException => error
|
49
|
+
raise DBI::DatabaseError.new(error.message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def prepare(sql)
|
53
|
+
if self["allow_scroll"]
|
54
|
+
return Statement.new(@connection.prepareStatement(sql,ResultSet::TYPE_SCROLL_INSENSITIVE, ResultSet::CONCUR_READ_ONLY), self["nulltype"], self["allow_scroll"])
|
55
|
+
else
|
56
|
+
return Statement.new(@connection.prepareStatement(sql), self["nulltype"], self["allow_scroll"])
|
57
|
+
end
|
58
|
+
rescue NativeException => error
|
59
|
+
raise DBI::DatabaseError.new(error.message)
|
60
|
+
end
|
61
|
+
|
62
|
+
def do(statement, *bindvars)
|
63
|
+
res = nil
|
64
|
+
if bindvars.nil? || bindvars.empty?
|
65
|
+
stmt = @connection.createStatement()
|
66
|
+
begin
|
67
|
+
stmt.execute(statement)
|
68
|
+
ensure
|
69
|
+
stmt.close rescue NativeException
|
70
|
+
end
|
71
|
+
else
|
72
|
+
stmt = execute(statement, *bindvars)
|
73
|
+
res = stmt.rows
|
74
|
+
stmt.finish
|
75
|
+
end
|
76
|
+
return res
|
77
|
+
rescue NativeException => error
|
78
|
+
raise DBI::DatabaseError.new(error.message)
|
79
|
+
end
|
80
|
+
|
81
|
+
def ping
|
82
|
+
return !@connection.isClosed
|
83
|
+
rescue NativeException => error
|
84
|
+
raise DBI::DatabaseError.new(error.message)
|
85
|
+
end
|
86
|
+
|
87
|
+
def commit
|
88
|
+
@connection.commit()
|
89
|
+
rescue NativeException => error
|
90
|
+
raise DBI::DatabaseError.new(error.message)
|
91
|
+
end
|
92
|
+
|
93
|
+
def rollback
|
94
|
+
@connection.rollback()
|
95
|
+
rescue NativeException => error
|
96
|
+
raise DBI::DatabaseError.new(error.message)
|
97
|
+
end
|
98
|
+
|
99
|
+
def tables
|
100
|
+
rs = @connection.getMetaData.getTables(nil, nil, nil, nil)
|
101
|
+
tables = []
|
102
|
+
while(rs.next())
|
103
|
+
tables << rs.getString(3)
|
104
|
+
end
|
105
|
+
return tables
|
106
|
+
rescue NativeException => error
|
107
|
+
raise DBI::DatabaseError.new(error.message)
|
108
|
+
end
|
109
|
+
|
110
|
+
def columns(table)
|
111
|
+
(table,schema,db) = table.split(".").reverse
|
112
|
+
|
113
|
+
metaData = @connection.getMetaData()
|
114
|
+
rs = metaData.getColumns(db, schema, table, nil)
|
115
|
+
columns = []
|
116
|
+
while(rs.next())
|
117
|
+
type_name, dbi_type = jdbc_to_dbi_sqltype(rs.getShort(5))
|
118
|
+
columns << {
|
119
|
+
"name" => rs.getString(4),
|
120
|
+
"sql_type" => type_name,
|
121
|
+
"type_name" => rs.getString(6),
|
122
|
+
"precision" => rs.getInt(7),
|
123
|
+
"scale" => rs.getInt(9),
|
124
|
+
"default" => rs.getString(13),
|
125
|
+
"nullable" => (rs.getInt(11) == 1)
|
126
|
+
}
|
127
|
+
columns[-1]["dbi_type"] = dbi_type if dbi_type
|
128
|
+
end
|
129
|
+
return columns
|
130
|
+
rescue NativeException => error
|
131
|
+
raise DBI::DatabaseError.new(error.message)
|
132
|
+
end
|
133
|
+
|
134
|
+
def [](attribute)
|
135
|
+
attribute = attribute.downcase
|
136
|
+
check_attribute(attribute)
|
137
|
+
case attribute
|
138
|
+
when "autocommit" then @connection.getAutoCommit()
|
139
|
+
when "isolation" || "isolation_level" then @connection.getTransactionIsolation()
|
140
|
+
else
|
141
|
+
@attributes[attribute]
|
142
|
+
end
|
143
|
+
rescue NativeException => error
|
144
|
+
raise DBI::DatabaseError.new(error.message)
|
145
|
+
end
|
146
|
+
|
147
|
+
def []=(attribute, value)
|
148
|
+
attribute = attribute.downcase
|
149
|
+
check_attribute(attribute)
|
150
|
+
case attribute
|
151
|
+
when "autocommit" then @connection.setAutoCommit(value)
|
152
|
+
when "isolation" || "isolation_level" then @connection.setTransactionIsolation(value)
|
153
|
+
else
|
154
|
+
@attributes[attribute] = value
|
155
|
+
end
|
156
|
+
rescue NativeException => error
|
157
|
+
raise DBI::DatabaseError.new(error.message)
|
158
|
+
end
|
159
|
+
|
160
|
+
def java_connection
|
161
|
+
return @connection
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.from_java_connection(java_connection, type_coercion = false)
|
165
|
+
DBI::DatabaseHandle.new(Database.new(java_connection), type_coercion)
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def check_attribute(attribute)
|
171
|
+
raise DBI::NotSupportedError.new("Database attribute #{attribute} is not supported") unless (attribute == "autocommit" ||
|
172
|
+
attribute == "isolation" ||
|
173
|
+
attribute == "isolation_level" ||
|
174
|
+
@attributes.has_key?(attribute))
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end # module DBI
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# (C) 2009 Chad Johnson <chad.j.johnson@gmail.com>.
|
2
|
+
# (C) 2008 Ola Bini <ola.bini@gmail.com>.
|
3
|
+
# (C) 2006 Kristopher Schmidt <krisschmidt@optonline.net>.
|
4
|
+
#
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
# Redistribution and use in source and binary forms, with or without
|
8
|
+
# modification, are permitted provided that the following conditions
|
9
|
+
# are met:
|
10
|
+
# 1. Redistributions of source code must retain the above copyright
|
11
|
+
# notice, this list of conditions and the following disclaimer.
|
12
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
# 3. The name of the author may not be used to endorse or promote products
|
16
|
+
# derived from this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
19
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
20
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
21
|
+
# THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
22
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
23
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
24
|
+
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
26
|
+
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
27
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
module DBI::DBD::Jdbc
|
30
|
+
#
|
31
|
+
# Models the DBI::BaseDriver API to create DBI::DriverHandle objects.
|
32
|
+
#
|
33
|
+
class Driver < DBI::BaseDriver
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
super("0.4.0")
|
37
|
+
#attributes specific to this class. All attributes in the
|
38
|
+
#connect attributes that aren't in this list will be
|
39
|
+
#applied to the database handle
|
40
|
+
@driverAttributes = [ "driver" ]
|
41
|
+
@loaded_drivers = Collections.synchronizedMap(HashMap.new)
|
42
|
+
end
|
43
|
+
|
44
|
+
def load(name)
|
45
|
+
unless @loaded_drivers.containsKey(name)
|
46
|
+
clazz = java.lang.Class.forName(name,true,JRuby.runtime.jruby_class_loader)
|
47
|
+
java.sql.DriverManager.registerDriver(clazz.newInstance)
|
48
|
+
|
49
|
+
@loaded_drivers.put(name,true)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def connect(dbname, user, auth, attr)
|
54
|
+
driverClass = attr["driver"]
|
55
|
+
raise InterfaceError.new('driver class name must be specified as "driver" in connection attributes') unless driverClass
|
56
|
+
|
57
|
+
load(driverClass)
|
58
|
+
|
59
|
+
connection = java.sql.DriverManager.getConnection("jdbc:"+dbname, user, auth)
|
60
|
+
dbh = Database.new(connection)
|
61
|
+
|
62
|
+
(attr.keys - @driverAttributes).each { |key| dbh[key] = attr[key] }
|
63
|
+
|
64
|
+
return dbh
|
65
|
+
rescue NativeException => error
|
66
|
+
raise DBI::DatabaseError.new(error.message)
|
67
|
+
end
|
68
|
+
|
69
|
+
end #Driver
|
70
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# (C) 2009 Chad Johnson <chad.j.johnson@gmail.com>.
|
2
|
+
# (C) 2008 Ola Bini <ola.bini@gmail.com>.
|
3
|
+
# (C) 2006 Kristopher Schmidt <krisschmidt@optonline.net>.
|
4
|
+
#
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
# Redistribution and use in source and binary forms, with or without
|
8
|
+
# modification, are permitted provided that the following conditions
|
9
|
+
# are met:
|
10
|
+
# 1. Redistributions of source code must retain the above copyright
|
11
|
+
# notice, this list of conditions and the following disclaimer.
|
12
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
# 3. The name of the author may not be used to endorse or promote products
|
16
|
+
# derived from this software without specific prior written permission.
|
17
|
+
#
|
18
|
+
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
19
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
20
|
+
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
21
|
+
# THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
22
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
23
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
24
|
+
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
25
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
26
|
+
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
27
|
+
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
module DBI::DBD::Jdbc
|
30
|
+
#
|
31
|
+
# Models the DBI::BaseStatement API to create DBI::StatementHandle objects.
|
32
|
+
#
|
33
|
+
class Statement < DBI::BaseStatement
|
34
|
+
include TypeConversions
|
35
|
+
|
36
|
+
def initialize(statement, nulltype, allow_scroll = false)
|
37
|
+
@statement = statement
|
38
|
+
@nulltype = nulltype
|
39
|
+
@allow_scroll = allow_scroll
|
40
|
+
@rows = nil
|
41
|
+
@data = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def bind_param(param, value, attribs)
|
45
|
+
raise InterfaceError.new("Statement.bind_param only supports numeric placeholder numbers") unless param.is_a?(Fixnum)
|
46
|
+
if value.nil?
|
47
|
+
@statement.setNull(param, @nulltype)
|
48
|
+
elsif value.is_a?(String)
|
49
|
+
@statement.setString(param, value)
|
50
|
+
elsif value.is_a?(Fixnum)
|
51
|
+
#no reason not to coerce it to a long?
|
52
|
+
@statement.setLong(param, value)
|
53
|
+
elsif value.is_a?(Float)
|
54
|
+
#despite DBD spec saying Float->SQL Float, using Double gives
|
55
|
+
#better precision and passes tests that setFloat does not.
|
56
|
+
@statement.setDouble(param, value)
|
57
|
+
elsif value.is_a?(::DateTime) || value.is_a?(DBI::Timestamp)
|
58
|
+
@statement.setTimestamp(param, timestamp_to_jdbctimestamp(value))
|
59
|
+
elsif value.is_a?(::Date) || value.is_a?(DBI::Date)
|
60
|
+
@statement.setDate(param, date_to_jdbcdate(value))
|
61
|
+
elsif value.is_a?(::Time) || value.is_a?(DBI::Time)
|
62
|
+
@statement.setTime(param, time_to_jdbctime(value))
|
63
|
+
else
|
64
|
+
@statement.setObject(param, value)
|
65
|
+
end
|
66
|
+
rescue NativeException => error
|
67
|
+
raise DBI::DatabaseError.new(error.message)
|
68
|
+
end
|
69
|
+
|
70
|
+
def execute
|
71
|
+
if @statement.execute()
|
72
|
+
@rs = @statement.getResultSet
|
73
|
+
@rows = nil
|
74
|
+
@data.clear
|
75
|
+
else
|
76
|
+
@rs = nil
|
77
|
+
@rows = @statement.getUpdateCount
|
78
|
+
end
|
79
|
+
rescue NativeException => error
|
80
|
+
raise DBI::DatabaseError.new(error.message)
|
81
|
+
end
|
82
|
+
|
83
|
+
def finish
|
84
|
+
@statement.close()
|
85
|
+
@rs = nil
|
86
|
+
@rows = nil
|
87
|
+
rescue NativeException => error
|
88
|
+
raise DBI::DatabaseError.new(error.message)
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch
|
92
|
+
if (@rs && @rs.next())
|
93
|
+
return fill_data
|
94
|
+
else
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
rescue NativeException => error
|
98
|
+
raise DBI::DatabaseError.new(error.message)
|
99
|
+
end
|
100
|
+
|
101
|
+
def fill_data
|
102
|
+
@data.clear
|
103
|
+
metaData = @rs.getMetaData()
|
104
|
+
(1..metaData.getColumnCount()).each do |columnNumber|
|
105
|
+
@data << get_value(columnNumber, @rs, metaData)
|
106
|
+
end
|
107
|
+
return @data
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Unless "allow_scroll" was set on this connection this will default to the
|
112
|
+
# DBI::BaseStatement#fetch_scroll implementation.
|
113
|
+
#
|
114
|
+
# See DBI::BaseStatement#fetch_scroll. These additional constants are supported
|
115
|
+
# when "allow_scroll" is set on the connection.
|
116
|
+
#
|
117
|
+
# * DBI::SQL_FETCH_PRIOR: Fetch the row previous to the current one.
|
118
|
+
# * DBI::SQL_FETCH_FIRST: Fetch the first row.
|
119
|
+
# * DBI::SQL_FETCH_ABSOLUTE: Fetch the row at the offset provided.
|
120
|
+
# * DBI::SQL_FETCH_RELATIVE: Fetch the row at the current point + offset.
|
121
|
+
#
|
122
|
+
def fetch_scroll(direction, offset)
|
123
|
+
return super(direction, offset) unless @allow_scroll
|
124
|
+
|
125
|
+
case direction
|
126
|
+
when DBI::SQL_FETCH_NEXT
|
127
|
+
fill_data if @rs && @rs.next()
|
128
|
+
when DBI::SQL_FETCH_PRIOR
|
129
|
+
fill_data if @rs && @rs.previous()
|
130
|
+
when DBI::SQL_FETCH_FIRST
|
131
|
+
fill_data if @rs && @rs.first()
|
132
|
+
when DBI::SQL_FETCH_LAST
|
133
|
+
fill_data if @rs && @rs.last()
|
134
|
+
when DBI::SQL_FETCH_ABSOLUTE
|
135
|
+
fill_data if @rs && @rs.absolute(offset)
|
136
|
+
when DBI::SQL_FETCH_RELATIVE
|
137
|
+
fill_data if @rs && @rs.relative(offset)
|
138
|
+
else
|
139
|
+
raise DBI::NotSupportedError
|
140
|
+
end
|
141
|
+
rescue NativeException => error
|
142
|
+
raise DBI::NotSupportedError.new(error.message)
|
143
|
+
end
|
144
|
+
|
145
|
+
def column_info
|
146
|
+
info = Array.new
|
147
|
+
return info unless @rs
|
148
|
+
metaData = @rs.getMetaData()
|
149
|
+
(1..metaData.getColumnCount()).each do |columnNumber|
|
150
|
+
type_name, dbi_type = jdbc_to_dbi_sqltype(metaData.getColumnType(columnNumber))
|
151
|
+
info << {
|
152
|
+
"name" => metaData.getColumnName(columnNumber),
|
153
|
+
"sql_type" => type_name,
|
154
|
+
"type_name" => metaData.getColumnTypeName(columnNumber),
|
155
|
+
"precision" => metaData.getPrecision(columnNumber),
|
156
|
+
"scale" => metaData.getScale(columnNumber),
|
157
|
+
"nullable" => (metaData.isNullable(columnNumber) == 1)
|
158
|
+
}
|
159
|
+
info[-1]["dbi_type"] = dbi_type if dbi_type
|
160
|
+
end
|
161
|
+
return info
|
162
|
+
rescue NativeException => error
|
163
|
+
raise DBI::DatabaseError.new(error.message)
|
164
|
+
end
|
165
|
+
|
166
|
+
def rows
|
167
|
+
return @rows
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.from_java_statement(java_statement, type_coercion = false, null_type = java.sql.Types::VARCHAR)
|
171
|
+
raise DBI::DatabaseError.new("Only java.sql.PreparedStatement instances accepted") unless java_statement.kind_of?(java.sql.PreparedStatement)
|
172
|
+
|
173
|
+
DBI::StatementHandle.new(Statement.new(java_statement, null_type), true, true, type_coercion)
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def get_value(columnNumber, rs, metaData)
|
179
|
+
#note only map things that seem unlikely to coerce properly to jruby,
|
180
|
+
#since anything mapped as primitive type cannot be returned as null
|
181
|
+
return case metaData.getColumnType(columnNumber)
|
182
|
+
when java.sql.Types::BIT
|
183
|
+
rs.getBoolean(columnNumber)
|
184
|
+
when java.sql.Types::NUMERIC, java.sql.Types::DECIMAL
|
185
|
+
metaData.getScale(columnNumber) == 0 ? rs.getLong(columnNumber) : rs.getDouble(columnNumber)
|
186
|
+
when java.sql.Types::DATE
|
187
|
+
jdbcdate_to_date(rs.getDate(columnNumber))
|
188
|
+
when java.sql.Types::TIME
|
189
|
+
jdbctime_to_time(rs.getTime(columnNumber))
|
190
|
+
when java.sql.Types::TIMESTAMP
|
191
|
+
jdbctimestamp_to_timestamp(rs.getTimestamp(columnNumber))
|
192
|
+
else
|
193
|
+
rs.getObject(columnNumber)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end # class Statement
|
198
|
+
|
199
|
+
end # module Jdbc
|
@@ -26,32 +26,31 @@
|
|
26
26
|
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
27
27
|
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
28
|
|
29
|
-
module
|
30
|
-
|
29
|
+
module DBI::DBD::Jdbc::TypeConversions
|
31
30
|
include_class 'java.util.Calendar'
|
32
31
|
include_class 'java.sql.Types'
|
33
32
|
|
34
33
|
def jdbc_to_dbi_sqltype(jdbctype)
|
35
34
|
return case jdbctype
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
35
|
+
when Types::BIGINT then [DBI::SQL_BIGINT, nil]
|
36
|
+
when Types::BINARY then [DBI::SQL_BINARY, nil]
|
37
|
+
when Types::BIT then [DBI::SQL_BIT, nil]
|
38
|
+
when Types::CHAR then [DBI::SQL_CHAR, nil]
|
39
|
+
when Types::DATE then [DBI::SQL_DATE, DBI::DBD::Jdbc::Type::Timestamp]
|
40
|
+
when Types::DECIMAL then [DBI::SQL_DECIMAL, nil]
|
41
|
+
when Types::DOUBLE then [DBI::SQL_DOUBLE, nil]
|
42
|
+
when Types::FLOAT then [DBI::SQL_FLOAT, nil]
|
43
|
+
when Types::INTEGER then [DBI::SQL_INTEGER, nil]
|
44
|
+
when Types::LONGVARBINARY then [DBI::SQL_LONGVARBINARY, nil]
|
45
|
+
when Types::LONGVARCHAR then [DBI::SQL_LONGVARCHAR, nil]
|
46
|
+
when Types::NUMERIC then [DBI::SQL_NUMERIC, nil]
|
47
|
+
when Types::REAL then [DBI::SQL_REAL, nil]
|
48
|
+
when Types::SMALLINT then [DBI::SQL_SMALLINT, nil]
|
49
|
+
when Types::TIME then [DBI::SQL_TIME, DBI::DBD::Jdbc::Type::Timestamp]
|
50
|
+
when Types::TIMESTAMP then [DBI::SQL_TIMESTAMP, DBI::DBD::Jdbc::Type::Timestamp]
|
51
|
+
when Types::TINYINT then [DBI::SQL_TINYINT, nil]
|
52
|
+
when Types::VARBINARY then [DBI::SQL_VARBINARY, nil]
|
53
|
+
when Types::VARCHAR then [DBI::SQL_VARCHAR, nil]
|
55
54
|
else
|
56
55
|
[DBI::SQL_OTHER, nil]
|
57
56
|
end
|
metadata
CHANGED
@@ -18,16 +18,21 @@ name: dbd-jdbc
|
|
18
18
|
rdoc_options:
|
19
19
|
- --main
|
20
20
|
- README.txt
|
21
|
-
autorequire:
|
22
21
|
rubyforge_project: jruby-extras
|
22
|
+
autorequire:
|
23
|
+
licenses: []
|
24
|
+
|
23
25
|
executables: []
|
24
26
|
|
25
|
-
description:
|
26
|
-
specification_version:
|
27
|
+
description: A JDBC DBD driver for Ruby DBI
|
28
|
+
specification_version: 3
|
27
29
|
default_executable:
|
28
30
|
files:
|
29
|
-
- lib/dbd/JdbcTypeConversions.rb
|
30
31
|
- lib/dbd/Jdbc.rb
|
32
|
+
- lib/dbd/jdbc/database.rb
|
33
|
+
- lib/dbd/jdbc/driver.rb
|
34
|
+
- lib/dbd/jdbc/statement.rb
|
35
|
+
- lib/dbd/jdbc/type_conversion.rb
|
31
36
|
- Rakefile
|
32
37
|
- README.txt
|
33
38
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
@@ -38,17 +43,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
43
|
version:
|
39
44
|
extensions: []
|
40
45
|
|
41
|
-
rubygems_version: 1.3.
|
46
|
+
rubygems_version: 1.3.2
|
42
47
|
requirements: []
|
43
48
|
|
44
49
|
authors:
|
45
50
|
- Chad Johnson
|
46
|
-
date: 2009-
|
51
|
+
date: 2009-05-16 05:00:00 +00:00
|
47
52
|
platform: ruby
|
48
53
|
test_files: []
|
49
54
|
|
50
55
|
version: !ruby/object:Gem::Version
|
51
|
-
version: 0.0.
|
56
|
+
version: 0.0.5
|
52
57
|
require_paths:
|
53
58
|
- lib
|
54
59
|
dependencies: []
|