dbd-jdbc 0.1.6-java

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dbd-jdbc.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,27 @@
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.
data/README.txt ADDED
@@ -0,0 +1,85 @@
1
+ RECENT CHANGES
2
+ ==============
3
+ The dbd-jdbc project has recently moved to Github and can be found at the following url
4
+ (http://github.com/chadj/dbd-jdbc). Please report any bugs found to the issue
5
+ tracker on Github.
6
+
7
+ OVERVIEW
8
+ ========
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)
14
+
15
+ INSTALLATION
16
+ ============
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.
23
+
24
+ USAGE
25
+ =====
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):
31
+
32
+ dbh = DBI.connect('dbi:jdbc:mysql://localhost:3306/test', 'anonymous', '',
33
+ { "driver" => "com.mysql.jdbc.Driver" } )
34
+
35
+ SUPPORTED ATTRIBUTES
36
+ ====================
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
47
+
48
+ LIMITATIONS
49
+ ===========
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.
53
+
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.
63
+
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).
73
+
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 ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/dbd-jdbc.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dbd/jdbc/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "dbd-jdbc"
8
+ gem.version = DBI::DBD::Jdbc::VERSION
9
+ gem.authors = ["Chad Johnson"]
10
+ gem.email = ["chad.j.johnson@gmail.com"]
11
+ gem.description = %q{A JDBC DBD driver for Ruby DBI}
12
+ gem.summary = %q{JDBC driver for DBI, originally by Kristopher Schmidt and Ola Bini}
13
+ gem.homepage = "http://github.com/chadj/dbd-jdbc"
14
+ gem.rubyforge_project = 'jruby-extras'
15
+ gem.platform = Gem::Platform::JAVA
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_development_dependency 'rake'
20
+
21
+ # Not adding dbi as a hard dependency, since either the "real" dbi gem
22
+ # or rails-dbi will work.
23
+ gem.requirements << 'dbi or ruby-dbi gem'
24
+ end
data/lib/dbd/Jdbc.rb ADDED
@@ -0,0 +1,103 @@
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
+ require 'java'
30
+ require 'dbi'
31
+
32
+ module DBI
33
+ module DBD
34
+ #
35
+ # DBD::Jdbc - Database Driver for Java / JDBC
36
+ #
37
+ # Requires DBI and JRuby
38
+ #
39
+ module Jdbc
40
+ java_import 'java.sql.Connection'
41
+ java_import 'java.sql.ResultSet'
42
+ java_import 'java.util.HashMap'
43
+ java_import 'java.util.Collections'
44
+
45
+ #
46
+ # Transaction isolation levels copied from JDBC
47
+ #
48
+ TRANSACTION_NONE = Connection::TRANSACTION_NONE
49
+ TRANSACTION_READ_COMMITTED = Connection::TRANSACTION_READ_COMMITTED
50
+ TRANSACTION_READ_UNCOMMITTED = Connection::TRANSACTION_READ_UNCOMMITTED
51
+ TRANSACTION_REPEATABLE_READ = Connection::TRANSACTION_REPEATABLE_READ
52
+ TRANSACTION_SERIALIZABLE = Connection::TRANSACTION_SERIALIZABLE
53
+ # Convience isolation levels
54
+ NONE = Connection::TRANSACTION_NONE
55
+ READ_COMMITTED = Connection::TRANSACTION_READ_COMMITTED
56
+ READ_UNCOMMITTED = Connection::TRANSACTION_READ_UNCOMMITTED
57
+ REPEATABLE_READ = Connection::TRANSACTION_REPEATABLE_READ
58
+ SERIALIZABLE = Connection::TRANSACTION_SERIALIZABLE
59
+
60
+ #
61
+ # returns 'Jdbc'
62
+ #
63
+ # See DBI::TypeUtil#convert for more information.
64
+ #
65
+ def self.driver_name
66
+ "Jdbc"
67
+ end
68
+
69
+ module Type
70
+ class Timestamp < DBI::Type::Null
71
+ def self.parse(obj)
72
+ obj = super
73
+ return obj unless obj
74
+
75
+ if obj.kind_of?(::DateTime) || obj.kind_of?(::Time) || obj.kind_of?(::Date)
76
+ return obj
77
+ elsif obj.kind_of?(::String)
78
+ return ::DateTime.strptime(obj, "%Y-%m-%d %H:%M:%S")
79
+ else
80
+ return ::DateTime.parse(obj.to_s) if obj.respond_to? :to_s
81
+ return ::DateTime.parse(obj.to_str) if obj.respond_to? :to_str
82
+ return obj
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ #
89
+ # Bound values pass through this function before being handed to Statement.bind_param
90
+ #
91
+ # The false value prevents the default type conversion from occurring
92
+ #
93
+ DBI::TypeUtil.register_conversion(driver_name) do |obj|
94
+ [obj, false]
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ require 'dbd/jdbc/type_conversion'
101
+ require 'dbd/jdbc/driver'
102
+ require 'dbd/jdbc/database'
103
+ require 'dbd/jdbc/statement'
@@ -0,0 +1,180 @@
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 = true)
165
+ dbh = DBI::DatabaseHandle.new(Database.new(java_connection), type_coercion)
166
+ dbh.driver_name = "Jdbc"
167
+ dbh
168
+ end
169
+
170
+ private
171
+
172
+ def check_attribute(attribute)
173
+ raise DBI::NotSupportedError.new("Database attribute #{attribute} is not supported") unless (attribute == "autocommit" ||
174
+ attribute == "isolation" ||
175
+ attribute == "isolation_level" ||
176
+ @attributes.has_key?(attribute))
177
+ end
178
+
179
+ end
180
+ 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 DBI::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,212 @@
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 DBI::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
+ #
50
+ # The syntax below appears to be the best way to ensure that
51
+ # RubyStrings get converted to Java Strings correctly if it
52
+ # contains UTF-8.
53
+ #
54
+ # java.lang.String.new() will assume the system default
55
+ # encoding when converting the RubyString bytes ....
56
+ #
57
+ @statement.setString(param, java.lang.String.new(value))
58
+ elsif value.is_a?(Fixnum)
59
+ #no reason not to coerce it to a long?
60
+ @statement.setLong(param, value)
61
+ elsif value.is_a?(Float)
62
+ #despite DBD spec saying Float->SQL Float, using Double gives
63
+ #better precision and passes tests that setFloat does not.
64
+ @statement.setDouble(param, value)
65
+ elsif value.is_a?(::DateTime) || value.is_a?(DBI::Timestamp)
66
+ @statement.setTimestamp(param, timestamp_to_jdbctimestamp(value))
67
+ elsif value.is_a?(::Date) || value.is_a?(DBI::Date)
68
+ @statement.setDate(param, date_to_jdbcdate(value))
69
+ elsif value.is_a?(::Time) || value.is_a?(DBI::Time)
70
+ @statement.setTime(param, time_to_jdbctime(value))
71
+ else
72
+ @statement.setObject(param, value)
73
+ end
74
+ rescue NativeException => error
75
+ raise DBI::DatabaseError.new(error.message)
76
+ end
77
+
78
+ def execute
79
+ if @statement.execute()
80
+ @rs = @statement.getResultSet
81
+ @rows = nil
82
+ @data.clear
83
+ else
84
+ @rs = nil
85
+ @rows = @statement.getUpdateCount
86
+ end
87
+ rescue NativeException => error
88
+ raise DBI::DatabaseError.new(error.message)
89
+ end
90
+
91
+ def finish
92
+ @statement.close()
93
+ @rs = nil
94
+ @rows = nil
95
+ rescue NativeException => error
96
+ raise DBI::DatabaseError.new(error.message)
97
+ end
98
+
99
+ def fetch
100
+ if (@rs && @rs.next())
101
+ return fill_data
102
+ else
103
+ return nil
104
+ end
105
+ rescue NativeException => error
106
+ raise DBI::DatabaseError.new(error.message)
107
+ end
108
+
109
+ def fill_data
110
+ @data.clear
111
+ metaData = @rs.getMetaData()
112
+ (1..metaData.getColumnCount()).each do |columnNumber|
113
+ @data << get_value(columnNumber, @rs, metaData)
114
+ end
115
+ return @data
116
+ end
117
+
118
+ #
119
+ # Unless "allow_scroll" was set on this connection this will default to the
120
+ # DBI::BaseStatement#fetch_scroll implementation.
121
+ #
122
+ # See DBI::BaseStatement#fetch_scroll. These additional constants are supported
123
+ # when "allow_scroll" is set on the connection.
124
+ #
125
+ # * DBI::SQL_FETCH_PRIOR: Fetch the row previous to the current one.
126
+ # * DBI::SQL_FETCH_FIRST: Fetch the first row.
127
+ # * DBI::SQL_FETCH_ABSOLUTE: Fetch the row at the offset provided.
128
+ # * DBI::SQL_FETCH_RELATIVE: Fetch the row at the current point + offset.
129
+ #
130
+ def fetch_scroll(direction, offset)
131
+ return super(direction, offset) unless @allow_scroll
132
+
133
+ case direction
134
+ when DBI::SQL_FETCH_NEXT
135
+ fill_data if @rs && @rs.next()
136
+ when DBI::SQL_FETCH_PRIOR
137
+ fill_data if @rs && @rs.previous()
138
+ when DBI::SQL_FETCH_FIRST
139
+ fill_data if @rs && @rs.first()
140
+ when DBI::SQL_FETCH_LAST
141
+ fill_data if @rs && @rs.last()
142
+ when DBI::SQL_FETCH_ABSOLUTE
143
+ fill_data if @rs && @rs.absolute(offset)
144
+ when DBI::SQL_FETCH_RELATIVE
145
+ fill_data if @rs && @rs.relative(offset)
146
+ else
147
+ raise DBI::NotSupportedError
148
+ end
149
+ rescue NativeException => error
150
+ raise DBI::NotSupportedError.new(error.message)
151
+ end
152
+
153
+ def column_info
154
+ info = Array.new
155
+ return info unless @rs
156
+ metaData = @rs.getMetaData()
157
+ (1..metaData.getColumnCount()).each do |columnNumber|
158
+ type_name, dbi_type = jdbc_to_dbi_sqltype(metaData.getColumnType(columnNumber))
159
+ info << {
160
+ "name" => metaData.getColumnName(columnNumber),
161
+ "sql_type" => type_name,
162
+ "type_name" => metaData.getColumnTypeName(columnNumber),
163
+ "precision" => metaData.getPrecision(columnNumber),
164
+ "scale" => metaData.getScale(columnNumber),
165
+ "nullable" => (metaData.isNullable(columnNumber) == 1)
166
+ }
167
+ info[-1]["dbi_type"] = dbi_type if dbi_type
168
+ end
169
+ return info
170
+ rescue NativeException => error
171
+ raise DBI::DatabaseError.new(error.message)
172
+ end
173
+
174
+ def rows
175
+ return @rows
176
+ end
177
+
178
+ def self.from_java_statement(java_statement, type_coercion = false, null_type = java.sql.Types::VARCHAR)
179
+ raise DBI::DatabaseError.new("Only java.sql.PreparedStatement instances accepted") unless java_statement.kind_of?(java.sql.PreparedStatement)
180
+
181
+ DBI::StatementHandle.new(Statement.new(java_statement, null_type), true, true, type_coercion)
182
+ end
183
+
184
+ private
185
+
186
+ def get_value(columnNumber, rs, metaData)
187
+ #note only map things that seem unlikely to coerce properly to jruby,
188
+ #since anything mapped as primitive type cannot be returned as null
189
+ return case metaData.getColumnType(columnNumber)
190
+ when java.sql.Types::BIT
191
+ rs.getBoolean(columnNumber)
192
+ when java.sql.Types::NUMERIC, java.sql.Types::DECIMAL
193
+ # TODO: Find a better way to return Numerics and Decimals as nil when they are null in the DB
194
+ if rs.getObject(columnNumber)
195
+ metaData.getScale(columnNumber) == 0 ? rs.getLong(columnNumber) : rs.getDouble(columnNumber)
196
+ else
197
+ nil
198
+ end
199
+ when java.sql.Types::DATE
200
+ jdbcdate_to_date(rs.getDate(columnNumber))
201
+ when java.sql.Types::TIME
202
+ jdbctime_to_time(rs.getTime(columnNumber))
203
+ when java.sql.Types::TIMESTAMP
204
+ jdbctimestamp_to_timestamp(rs.getTimestamp(columnNumber))
205
+ else
206
+ rs.getObject(columnNumber)
207
+ end
208
+ end
209
+
210
+ end # class Statement
211
+
212
+ end # module Jdbc
@@ -0,0 +1,123 @@
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::TypeConversions
30
+ java_import 'java.util.Calendar'
31
+ java_import 'java.sql.Types'
32
+
33
+ def jdbc_to_dbi_sqltype(jdbctype)
34
+ return case jdbctype
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, DBI::Type::Boolean]
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]
54
+ else
55
+ [DBI::SQL_OTHER, nil]
56
+ end
57
+ end
58
+
59
+ def date_to_jdbcdate(dbidate)
60
+ cal = Calendar.getInstance()
61
+ set_calendar_date_fields(dbidate, cal)
62
+ return java.sql.Date.new(cal.getTime().getTime())
63
+ end
64
+
65
+ def time_to_jdbctime(dbitime)
66
+ cal = Calendar.getInstance()
67
+ set_calendar_date_fields(dbitime, cal)
68
+ set_calendar_time_fields(dbitime, cal)
69
+ return java.sql.Time.new(cal.getTime().getTime())
70
+ end
71
+
72
+ def timestamp_to_jdbctimestamp(dbitimestamp)
73
+ cal = Calendar.getInstance()
74
+ set_calendar_date_fields(dbitimestamp, cal)
75
+ set_calendar_time_fields(dbitimestamp, cal)
76
+ return java.sql.Timestamp.new(cal.getTime().getTime())
77
+ end
78
+
79
+ def jdbcdate_to_date(jdbcdate)
80
+ return nil if jdbcdate.nil?
81
+ cal = get_calendar(jdbcdate)
82
+ return ::Date.new(cal.get(Calendar::YEAR), cal.get(Calendar::MONTH)+1, cal.get(Calendar::DAY_OF_MONTH))
83
+ end
84
+
85
+ def jdbctime_to_time(jdbctime)
86
+ return nil if jdbctime.nil?
87
+ cal = get_calendar(jdbctime)
88
+ return ::Time.mktime(cal.get(Calendar::YEAR), cal.get(Calendar::MONTH)+1, cal.get(Calendar::DAY_OF_MONTH), cal.get(Calendar::HOUR_OF_DAY), cal.get(Calendar::MINUTE), cal.get(Calendar::SECOND), cal.get(Calendar::MILLISECOND) * 1000)
89
+ end
90
+
91
+ def jdbctimestamp_to_timestamp(jdbctimestamp)
92
+ return nil if jdbctimestamp.nil?
93
+ cal = get_calendar(jdbctimestamp)
94
+ return ::DateTime.new(cal.get(Calendar::YEAR), cal.get(Calendar::MONTH)+1, cal.get(Calendar::DAY_OF_MONTH), cal.get(Calendar::HOUR_OF_DAY), cal.get(Calendar::MINUTE), cal.get(Calendar::SECOND))
95
+ end
96
+
97
+ private
98
+
99
+ def set_calendar_date_fields(dbidate, cal)
100
+ cal.set(Calendar::YEAR, dbidate.year) if dbidate.respond_to? :year
101
+ cal.set(Calendar::MONTH, dbidate.month-1) if dbidate.respond_to? :month
102
+ cal.set(Calendar::DAY_OF_MONTH, dbidate.day) if dbidate.respond_to? :day
103
+ end
104
+
105
+ def set_calendar_time_fields(dbitime, cal)
106
+ cal.set(Calendar::HOUR_OF_DAY, dbitime.hour)
107
+ cal.set(Calendar::MINUTE, dbitime.min)
108
+ cal.set(Calendar::SECOND, dbitime.sec)
109
+
110
+ if dbitime.respond_to? :strftime
111
+ cal.set(Calendar::MILLISECOND, dbitime.strftime('%L').to_i)
112
+ else
113
+ cal.set(Calendar::MILLISECOND, 0)
114
+ end
115
+ end
116
+
117
+ def get_calendar(jdbctype)
118
+ cal = Calendar.getInstance()
119
+ cal.setTime(java.util.Date.new(jdbctype.getTime()))
120
+ return cal
121
+ end
122
+
123
+ end
@@ -0,0 +1,7 @@
1
+ module DBI
2
+ module DBD
3
+ module Jdbc
4
+ VERSION = "0.1.6"
5
+ end
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dbd-jdbc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ prerelease:
6
+ platform: java
7
+ authors:
8
+ - Chad Johnson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: !binary |-
21
+ MA==
22
+ none: false
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: !binary |-
28
+ MA==
29
+ none: false
30
+ prerelease: false
31
+ type: :development
32
+ description: A JDBC DBD driver for Ruby DBI
33
+ email:
34
+ - chad.j.johnson@gmail.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - .gitignore
40
+ - Gemfile
41
+ - LICENSE
42
+ - README.txt
43
+ - Rakefile
44
+ - dbd-jdbc.gemspec
45
+ - lib/dbd/Jdbc.rb
46
+ - lib/dbd/jdbc/database.rb
47
+ - lib/dbd/jdbc/driver.rb
48
+ - lib/dbd/jdbc/statement.rb
49
+ - lib/dbd/jdbc/type_conversion.rb
50
+ - lib/dbd/jdbc/version.rb
51
+ homepage: http://github.com/chadj/dbd-jdbc
52
+ licenses: []
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: !binary |-
64
+ MA==
65
+ hash: 2
66
+ none: false
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: !binary |-
74
+ MA==
75
+ hash: 2
76
+ none: false
77
+ requirements:
78
+ - dbi or ruby-dbi gem
79
+ rubyforge_project: jruby-extras
80
+ rubygems_version: 1.8.24
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: JDBC driver for DBI, originally by Kristopher Schmidt and Ola Bini
84
+ test_files: []