diff --git a/README.rst b/README.rst
index 4c54fb4..52e8f29 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,6 @@
-=====================================================================
+=================================================================
JayDeBeApi - bridge from JDBC database drivers to Python DB-API
-=====================================================================
+=================================================================
The JayDeBeApi module allows you to connect from Python code to
databases using Java `JDBC
@@ -11,10 +11,6 @@ It works on ordinary Python (cPython) using the JPype_ Java
integration or on `Jython `_ to make use of
the Java JDBC driver.
-It has been tested with `Hypersonic SQL (HSQLDB)
-`_ and `IBM DB2
-`_ for z/OS.
-
In contrast to zxJDBC from the Jython project JayDeBeApi let's you
access a database with Jython AND Python with only minor code
modifications. JayDeBeApi's future goal is to provide a unique and
@@ -88,6 +84,7 @@ in memory database on my Ubuntu machine I'm starting Python by running ::
Now I have to configure JPype
+>>> import jpype
>>> jar = '/path/to/my/driver/hsqldb.jar'
>>> args='-Djava.class.path=%s' % jar
>>> jpype.startJVM(jvm_path, args)
@@ -98,6 +95,19 @@ or in Jython I have to
>>> import sys
>>> sys.path.append(jar)
+Supported databases
+===================
+
+In theory every database with a suitable JDBC driver should work. It
+is known to work with the following databases:
+
+* `SQLite 3 `_ using `SqliteJDBC
+ `_ v056
+* `Hypersonic SQL (HSQLDB) `_ 1.8.1.3
+* `IBM DB2 `_ for z/OS using
+ JDBC type 4 drivers.
+
+
Contributing
============
@@ -116,18 +126,27 @@ distribution for details.
Changelog
=========
+- trunk
+
+ - Fixed DB-API_ violation: Use ``curs.execute('foo ?', (bar, baz))``
+ instead of ``curs.execute('foo ?', bar, baz)``.
+
+ - Free resources after ``executemany`` call.
+
+ - Improved type handling.
+
- 0.1.2
- - ``easy_install JayDeBeApi`` should really work
+ - ``easy_install JayDeBeApi`` should really work.
- 0.1.1
- - Fixed bug #688290 "NULL values with converters error on fetch."
- - Fixed bug #684909 "Selecting ROWIDs errors out on fetch."
+ - Fixed bug #688290 "NULL values with converters error on fetch".
+ - Fixed bug #684909 "Selecting ROWIDs errors out on fetch".
- 0.1
- - Initial release
+ - Initial release.
To do
=====
diff --git a/src/JayDeBeApi.egg-info/PKG-INFO b/src/JayDeBeApi.egg-info/PKG-INFO
index d317f46..4c151f7 100644
--- a/src/JayDeBeApi.egg-info/PKG-INFO
+++ b/src/JayDeBeApi.egg-info/PKG-INFO
@@ -7,7 +7,7 @@ Author: Bastian Bowe
Author-email: bastian.bowe@gmail.com
License: GNU LGPL
Description: =====================================================================
- JayDeBeApi - bridge from JDBC database drivers to Python DB-API
+ JayDeBeApi - bridge from JDBC database drivers to Python DB-API
=====================================================================
The JayDeBeApi module allows you to connect from Python code to
@@ -19,10 +19,6 @@ Description: ===================================================================
integration or on `Jython `_ to make use of
the Java JDBC driver.
- It has been tested with `Hypersonic SQL (HSQLDB)
- `_ and `IBM DB2
- `_ for z/OS.
-
In contrast to zxJDBC from the Jython project JayDeBeApi let's you
access a database with Jython AND Python with only minor code
modifications. JayDeBeApi's future goal is to provide a unique and
@@ -37,7 +33,7 @@ Description: ===================================================================
You can get and install JayDeBeApi with `easy_install
`_ ::
- $ easy_install JayDeBeApi
+ $ easy_install JayDeBeApi
If you want to install JayDeBeApi in Jython make sure to have
EasyInstall available for it.
@@ -45,15 +41,15 @@ Description: ===================================================================
Or you can get a copy of the source branch using `bzr
`_ by running ::
- $ bzr branch lp:jaydebeapi
+ $ bzr branch lp:jaydebeapi
and install it with ::
- $ python setup.py install
+ $ python setup.py install
or if you are using Jython use ::
- $ jython setup.py install
+ $ jython setup.py install
It has been tested with Jython 2.5.2.
@@ -92,10 +88,11 @@ Description: ===================================================================
access the database driver's jar files. If I want to connect to a HSQL
in memory database on my Ubuntu machine I'm starting Python by running ::
- $ JAVA_HOME=/usr/lib/jvm/java-6-openjdk python
+ $ JAVA_HOME=/usr/lib/jvm/java-6-openjdk python
Now I have to configure JPype
+ >>> import jpype
>>> jar = '/path/to/my/driver/hsqldb.jar'
>>> args='-Djava.class.path=%s' % jar
>>> jpype.startJVM(jvm_path, args)
@@ -106,6 +103,19 @@ Description: ===================================================================
>>> import sys
>>> sys.path.append(jar)
+ Supported databases
+ ===================
+
+ In theory every database with a suitable JDBC driver should work. It
+ is known to work with the following databases:
+
+ * `SQLite 3 `_ using `SqliteJDBC
+ `_ v056
+ * `Hypersonic SQL (HSQLDB) `_ 1.8.1.3
+ * `IBM DB2 `_ for z/OS using
+ JDBC type 4 drivers.
+
+
Contributing
============
@@ -124,27 +134,32 @@ Description: ===================================================================
Changelog
=========
+ - trunk
+
+ - Fixed DB-API_ violation: Use ``curs.execute('foo ?', (bar, baz))``
+ instead of ``curs.execute('foo ?', bar, baz)``.
+
- 0.1.2
- - ``easy_install JayDeBeApi`` should really work
+ - ``easy_install JayDeBeApi`` should really work
- 0.1.1
- - Fixed bug #688290 "NULL values with converters error on fetch."
- - Fixed bug #684909 "Selecting ROWIDs errors out on fetch."
+ - Fixed bug #688290 "NULL values with converters error on fetch."
+ - Fixed bug #684909 "Selecting ROWIDs errors out on fetch."
- 0.1
- - Initial release
+ - Initial release
To do
=====
- Extract Java calls to seperate Java methods to increase performance.
- Check if https://code.launchpad.net/dbapi-compliance can help making
- JayDeBeApi more DB-API complient.
+ JayDeBeApi more DB-API complient.
- Test it on different databases and provide a flexible db specific
- pluign mechanism.
+ pluign mechanism.
- SQLAlchemy modules (seperate project)
.. _DB-API: http://www.python.org/dev/peps/pep-0249/
diff --git a/src/jaydebeapi/dbapi2.py b/src/jaydebeapi/dbapi2.py
index 93ba276..d328318 100644
--- a/src/jaydebeapi/dbapi2.py
+++ b/src/jaydebeapi/dbapi2.py
@@ -233,14 +233,20 @@ class Cursor(object):
self._meta = None
self._description = None
- def _set_stmt_parms(self, prep_stmt, *parameters):
+ # TODO: this is a possible way to close the open result sets
+ # but I'm not sure when __del__ will be called
+ #__del__ = _close_last
+
+ def _set_stmt_parms(self, prep_stmt, parameters):
for i in range(len(parameters)):
prep_stmt.setObject(i + 1, parameters[i])
- def execute(self, operation, *parameters):
+ def execute(self, operation, parameters=None):
+ if not parameters:
+ parameters = ()
self._close_last()
self._prep = self._connection.jconn.prepareStatement(operation)
- self._set_stmt_parms(self._prep, *parameters)
+ self._set_stmt_parms(self._prep, parameters)
is_rs = self._prep.execute()
self.update_count = self._prep.getUpdateCount()
if is_rs:
@@ -252,11 +258,12 @@ class Cursor(object):
self._close_last()
self._prep = self._connection.jconn.prepareStatement(operation)
for parameters in seq_of_parameters:
- self._set_stmt_parms(self._prep, *parameters)
+ self._set_stmt_parms(self._prep, parameters)
self._prep.addBatch()
update_counts = self._prep.executeBatch()
# self._prep.getWarnings() ???
self.rowcount = sum(update_counts)
+ self._close_last()
def fetchone(self):
#raise if not rs
@@ -276,6 +283,7 @@ class Cursor(object):
def fetchmany(self, size=None):
if size is None:
size = self.arraysize
+ # TODO: handle SQLException if not supported by db
self._rs.setFetchSize(size)
rows = []
row = None
@@ -287,6 +295,7 @@ class Cursor(object):
rows.append(row)
# reset fetch size
if row:
+ # TODO: handle SQLException if not supported by db
self._rs.setFetchSize(0)
return rows
@@ -311,18 +320,21 @@ class Cursor(object):
pass
def to_datetime(java_val):
-# d=datetime.datetime.strptime(timestmp.toString()[:-7], "%Y-%m-%d %H:%M:%S")
-# return d.replace(microsecond=int(str(timestmp.getNanos())[:6]))
- return java_val.toString()
+ #d=datetime.datetime.strptime(str(java_val)[:-7], "%Y-%m-%d %H:%M:%S")
+ #return d.replace(microsecond=int(str(java_val.getNanos())[:6]))
+ return str(java_val)
def to_date(java_val):
-# d=datetime.datetime.strptime(timestmp.toString()[:-7], "%Y-%m-%d %H:%M:%S")
-# return d.replace(microsecond=int(str(timestmp.getNanos())[:6]))
- return java_val.toString()
+ #d=datetime.datetime.strptime(str(java_val)[:-7], "%Y-%m-%d %H:%M:%S")
+ #return d.replace(microsecond=int(str(java_val.getNanos())[:6]))
+ return str(java_val)
def to_float(java_val):
return java_val.doubleValue()
+def to_int(java_val):
+ return java_val.intValue()
+
def _init_converters(types_map):
"""Prepares the converters for conversion of java types to python
objects.
@@ -345,4 +357,5 @@ _DEFAULT_CONVERTERS = {
'DATE': to_date,
'DECIMAL': to_float,
'NUMERIC': to_float,
+ 'INTEGER': to_int,
}
diff --git a/src/test/integration_test.py b/src/test/integration_test.py
index 4dbf101..96b943a 100644
--- a/src/test/integration_test.py
+++ b/src/test/integration_test.py
@@ -17,19 +17,19 @@
# License along with JayDeBeApi. If not, see
# .
-import os
+from os import path
from unittest import TestCase
import jaydebeapi
import sys
-this_dir = os.path.dirname(os.path.abspath(__file__))
-jar_dir = os.path.abspath(os.path.join(this_dir, '..', '..',
+this_dir = path.dirname(path.abspath(__file__))
+jar_dir = path.abspath(path.join(this_dir, '..', '..',
'build', 'lib'))
-create_sql = os.path.join(this_dir, 'data', 'create.sql')
-insert_sql = os.path.join(this_dir, 'data', 'insert.sql')
+create_sql = path.join(this_dir, 'data', 'create.sql')
+insert_sql = path.join(this_dir, 'data', 'insert.sql')
def is_jython():
- return sys.platform.lower().startswith('java')
+ return sys.platform.lower().startswith('java')
class IntegrationTest(TestCase):
@@ -50,31 +50,52 @@ class IntegrationTest(TestCase):
for i in stmts:
cursor.execute(i)
- def setup_jpype(self, jars):
+ def setup_jpype(self, jars, libs=None):
import jpype
if not jpype.isJVMStarted():
jvm_path = jpype.getDefaultJVMPath()
#jvm_path = ('/usr/lib/jvm/java-6-openjdk'
# '/jre/lib/i386/client/libjvm.so')
- args='-Djava.class.path=%s' % jars
- jpype.startJVM(jvm_path, args)
+ # path to shared libraries
+ args = []
+ if libs:
+ libs_path = path.pathsep.join(libs)
+ args.append('-Djava.library.path=%s' % libs_path)
+ class_path = path.pathsep.join(jars)
+ args.append('-Djava.class.path=%s' % class_path)
+ jpype.startJVM(jvm_path, *args)
if not jpype.isThreadAttachedToJVM():
jpype.attachThreadToJVM()
- def setUp(self):
- # TODO support more than one jar
- #jars='/usr/share/java/hsqldb.jar'
- jars=os.path.join(jar_dir, 'hsqldb.jar')
- #jars=jars_path(r'C:\Programme\DbVisualizer-4.3.5\jdbc')
+ def connect(self):
+ import sqlite3
+ return sqlite3.connect(':memory:')
+
+ def connect_(self):
+ jar_names = [ 'hsqldb.jar', 'sqlitejdbc-v056.jar', 'sqlite.jar' ]
+ jars = [ path.join(jar_dir, i) for i in jar_names ]
if is_jython():
- sys.path.append(jars)
+ sys.path.extend(jars)
else:
- self.setup_jpype(jars)
- self.conn = jaydebeapi.connect('org.hsqldb.jdbcDriver',
- 'jdbc:hsqldb:mem', 'SA', '')
- #conn = jaydebeapi.connect('com.ibm.db2.jcc.DB2Driver',
- # 'jdbc:db2://4.100.73.81:50000/db2t',
- # getpass.getuser(), getpass.getpass())
+ self.setup_jpype(jars, [jar_dir])
+ # http://www.zentus.com/sqlitejdbc/
+ conn = jaydebeapi.connect('org.sqlite.JDBC',
+ 'jdbc:sqlite::memory:')
+ # http://hsqldb.org/
+ # conn = jaydebeapi.connect('org.hsqldb.jdbcDriver',
+ # 'jdbc:hsqldb:mem', 'SA', '')
+ # conn = jaydebeapi.connect('com.ibm.db2.jcc.DB2Driver',
+ # 'jdbc:db2://4.100.73.81:50000/db2t',
+ # getpass.getuser(),
+ # getpass.getpass())
+ # driver from http://www.ch-werner.de/javasqlite/ seems to be
+ # crap as it returns decimal values as VARCHAR type
+ # conn = jaydebeapi.connect('SQLite.JDBCDriver',
+ # 'jdbc:sqlite:/:memory:')
+ return conn
+
+ def setUp(self):
+ self.conn = self.connect()
self.sql_file(create_sql)
self.sql_file(insert_sql)
@@ -98,7 +119,7 @@ class IntegrationTest(TestCase):
def test_execute_and_fetch_parameter(self):
cursor = self.conn.cursor()
- cursor.execute("select * from konto where konto_nr = ?", 18)
+ cursor.execute("select * from konto where konto_nr = ?", (18,))
result = cursor.fetchall()
assert [(u'2009-09-10 14:15:22.123456', 18, 12.4, None)] == result
@@ -107,6 +128,7 @@ class IntegrationTest(TestCase):
cursor.execute("select * from konto order by konto_nr")
result = cursor.fetchone()
assert (u'2009-09-10 14:15:22.123456', 18, 12.4, None) == result
+ cursor.close()
def test_execute_reset_description_without_execute_result(self):
"""Excpect the descriptions property being reset when no query
@@ -121,7 +143,7 @@ class IntegrationTest(TestCase):
def test_execute_and_fetchone_after_end(self):
cursor = self.conn.cursor()
- cursor.execute("select * from konto where konto_nr = ?", 18)
+ cursor.execute("select * from konto where konto_nr = ?", (18,))
cursor.fetchone()
result = cursor.fetchone()
assert None is result
@@ -131,6 +153,9 @@ class IntegrationTest(TestCase):
cursor.execute("select * from konto order by konto_nr")
result = cursor.fetchmany()
assert [(u'2009-09-10 14:15:22.123456', 18, 12.4, None)] == result
+ # TODO: find out why this cursor has to be closed in order to
+ # let this test work with sqlite
+# cursor.close()
def test_executemany(self):
cursor = self.conn.cursor()
@@ -142,4 +167,3 @@ class IntegrationTest(TestCase):
)
cursor.executemany(stmt, parms)
assert cursor.rowcount == 3
-