Further type handling improvements. BLOB support. Free resources on cursor destruction.
parent
525c3fa192
commit
2695c9fe42
|
|
@ -133,7 +133,7 @@ Changelog
|
|||
|
||||
- Free resources after ``executemany`` call.
|
||||
|
||||
- Improved type handling.
|
||||
- Improved type handling. Initial support for BLOB columns.
|
||||
|
||||
- 0.1.2
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ Build a new release
|
|||
|
||||
3. Assert the right connect method is configured for tests.
|
||||
|
||||
4. Run test suite. ::
|
||||
4. Run test suite. Once for cPython, once for Jython and ideally
|
||||
against all accessible databases. ::
|
||||
|
||||
$ nosetests
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import sys
|
|||
|
||||
_jdbc_connect = None
|
||||
|
||||
_java_array_byte = None
|
||||
|
||||
def _jdbc_connect_jython(jclassname, *args):
|
||||
if _converters is None:
|
||||
from java.sql import Types
|
||||
|
|
@ -37,14 +39,17 @@ def _jdbc_connect_jython(jclassname, *args):
|
|||
_init_converters(types_map)
|
||||
# register driver for DriverManager
|
||||
__import__(jclassname)
|
||||
global _java_array_byte
|
||||
if _java_array_byte is None:
|
||||
import jarray
|
||||
def _java_array_byte(data):
|
||||
return jarray.array(data, 'b')
|
||||
from java.sql import DriverManager
|
||||
return DriverManager.getConnection(*args)
|
||||
|
||||
def _prepare_jython():
|
||||
global _jdbc_connect
|
||||
_jdbc_connect = _jdbc_connect_jython
|
||||
# TODO: find solution for jython
|
||||
# Binary = buffer
|
||||
|
||||
def _jdbc_connect_jpype(jclassname, *args):
|
||||
import jpype
|
||||
|
|
@ -58,6 +63,10 @@ def _jdbc_connect_jpype(jclassname, *args):
|
|||
for i in types.__javaclass__.getClassFields():
|
||||
types_map[i.getName()] = i.getStaticAttribute()
|
||||
_init_converters(types_map)
|
||||
global _java_array_byte
|
||||
if _java_array_byte is None:
|
||||
def _java_array_byte(data):
|
||||
return jpype.JArray(jpype.JByte, 1)(data)
|
||||
# register driver for DriverManager
|
||||
jpype.JClass(jclassname)
|
||||
return jpype.java.sql.DriverManager.getConnection(*args)
|
||||
|
|
@ -65,9 +74,6 @@ def _jdbc_connect_jpype(jclassname, *args):
|
|||
def _prepare_jpype():
|
||||
global _jdbc_connect
|
||||
_jdbc_connect = _jdbc_connect_jpype
|
||||
# TODO: doesn't work for Jython
|
||||
# global Binary
|
||||
# Binary = buffer
|
||||
|
||||
if sys.platform.lower().startswith('java'):
|
||||
_prepare_jython()
|
||||
|
|
@ -143,11 +149,22 @@ class NotSupportedError(DatabaseError):
|
|||
pass
|
||||
|
||||
# DB-API 2.0 Type Objects and Constructors
|
||||
Date = datetime.date
|
||||
|
||||
Time = datetime.time
|
||||
def _java_sql_blob(data):
|
||||
return _java_array_byte(data)
|
||||
|
||||
Timestamp = datetime.datetime
|
||||
Binary = _java_sql_blob
|
||||
|
||||
def _str_func(func):
|
||||
def to_str(*parms):
|
||||
return str(func(*parms))
|
||||
return to_str
|
||||
|
||||
Date = _str_func(datetime.date)
|
||||
|
||||
Time = _str_func(datetime.time)
|
||||
|
||||
Timestamp = _str_func(datetime.datetime)
|
||||
|
||||
def DateFromTicks(ticks):
|
||||
return apply(Date, time.localtime(ticks)[:3])
|
||||
|
|
@ -235,10 +252,11 @@ class Cursor(object):
|
|||
|
||||
# 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
|
||||
__del__ = _close_last
|
||||
|
||||
def _set_stmt_parms(self, prep_stmt, parameters):
|
||||
for i in range(len(parameters)):
|
||||
# print (i, parameters[i], type(parameters[i]))
|
||||
prep_stmt.setObject(i + 1, parameters[i])
|
||||
|
||||
def execute(self, operation, parameters=None):
|
||||
|
|
@ -272,6 +290,7 @@ class Cursor(object):
|
|||
row = []
|
||||
for col in range(1, self._meta.getColumnCount() + 1):
|
||||
sqltype = self._meta.getColumnType(col)
|
||||
# print sqltype
|
||||
v = self._rs.getObject(col)
|
||||
if v:
|
||||
converter = _converters.get(sqltype)
|
||||
|
|
@ -319,21 +338,31 @@ class Cursor(object):
|
|||
def setoutputsize(self, size, column):
|
||||
pass
|
||||
|
||||
def to_datetime(java_val):
|
||||
#d=datetime.datetime.strptime(str(java_val)[:-7], "%Y-%m-%d %H:%M:%S")
|
||||
#return d.replace(microsecond=int(str(java_val.getNanos())[:6]))
|
||||
def _to_datetime(java_val):
|
||||
d = datetime.datetime.strptime(str(java_val)[:19], "%Y-%m-%d %H:%M:%S")
|
||||
if not isinstance(java_val, basestring):
|
||||
d = d.replace(microsecond=int(str(java_val.getNanos())[:6]))
|
||||
return str(d)
|
||||
# return str(java_val)
|
||||
|
||||
def _to_date(java_val):
|
||||
d = datetime.datetime.strptime(str(java_val)[:10], "%Y-%m-%d")
|
||||
return d.strftime("%Y-%m-%d")
|
||||
# return str(java_val)
|
||||
|
||||
def _to_string(java_val):
|
||||
return str(java_val)
|
||||
|
||||
def to_date(java_val):
|
||||
#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 _java_to_py(java_method):
|
||||
def to_py(java_val):
|
||||
if isinstance(java_val, (basestring, int, long, float, bool)):
|
||||
return java_val
|
||||
return getattr(java_val, java_method)()
|
||||
return to_py
|
||||
|
||||
def to_float(java_val):
|
||||
return java_val.doubleValue()
|
||||
_to_double = _java_to_py('doubleValue')
|
||||
|
||||
def to_int(java_val):
|
||||
return java_val.intValue()
|
||||
_to_int = _java_to_py('intValue')
|
||||
|
||||
def _init_converters(types_map):
|
||||
"""Prepares the converters for conversion of java types to python
|
||||
|
|
@ -353,9 +382,14 @@ _DEFAULT_CONVERTERS = {
|
|||
# see
|
||||
# http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Types.html
|
||||
# for possible keys
|
||||
'TIMESTAMP': to_datetime,
|
||||
'DATE': to_date,
|
||||
'DECIMAL': to_float,
|
||||
'NUMERIC': to_float,
|
||||
'INTEGER': to_int,
|
||||
'TIMESTAMP': _to_datetime,
|
||||
'DATE': _to_date,
|
||||
'BINARY': _to_string,
|
||||
'DECIMAL': _to_double,
|
||||
'NUMERIC': _to_double,
|
||||
'DOUBLE': _to_double,
|
||||
'FLOAT': _to_double,
|
||||
'INTEGER': _to_int,
|
||||
'SMALLINT': _to_int,
|
||||
'BOOLEAN': _java_to_py('booleanValue'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ create table Account (
|
|||
"ACCOUNT_NO" INTEGER not null,
|
||||
"BALANCE" DECIMAL default 0.0 not null,
|
||||
"BLOCKING" DECIMAL,
|
||||
"DBL_COL" DOUBLE,
|
||||
"OPENED_AT" DATE,
|
||||
"VALID" BOOLEAN,
|
||||
"PRODUCT_NAME" VARCHAR,
|
||||
"STUFF" BLOB,
|
||||
primary key ("ACCOUNT_ID")
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
insert into ACCOUNT values ('2009-09-10 14:15:22.123456', 18, 12.4, null);
|
||||
insert into ACCOUNT values ('2009-09-11 14:15:22.123456', 19, 12.9, 1);
|
||||
insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING) values ('2009-09-10 14:15:22.123456', 18, 12.4, null);
|
||||
insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING) values ('2009-09-11 14:15:22.123456', 19, 12.9, 1);
|
||||
|
|
|
|||
|
|
@ -73,13 +73,14 @@ class IntegrationTest(TestCase):
|
|||
msg = "Warinng: Your are not running the tests against JayDeBeApi."
|
||||
print >> sys.stderr, msg
|
||||
import sqlite3
|
||||
return sqlite3.connect(':memory:')
|
||||
return sqlite3, sqlite3.connect(':memory:')
|
||||
|
||||
def connect(self):
|
||||
jar_names = [ 'sqlitejdbc-v056.jar', 'hsqldb.jar', 'sqlite.jar' ]
|
||||
jars = [ path.join(jar_dir, i) for i in jar_names ]
|
||||
if is_jython():
|
||||
sys.path.extend(jars)
|
||||
# print "CLASSPATH=%s" % path.pathsep.join(jars)
|
||||
else:
|
||||
self.setup_jpype(jars, [jar_dir])
|
||||
# http://www.zentus.com/sqlitejdbc/
|
||||
|
|
@ -96,10 +97,10 @@ class IntegrationTest(TestCase):
|
|||
# crap as it returns decimal values as VARCHAR type
|
||||
# conn = jaydebeapi.connect('SQLite.JDBCDriver',
|
||||
# 'jdbc:sqlite:/:memory:')
|
||||
return conn
|
||||
return jaydebeapi, conn
|
||||
|
||||
def setUp(self):
|
||||
self.conn = self.connect()
|
||||
(self.dbapi, self.conn) = self.connect()
|
||||
self.sql_file(create_sql)
|
||||
self.sql_file(insert_sql)
|
||||
|
||||
|
|
@ -116,20 +117,23 @@ class IntegrationTest(TestCase):
|
|||
|
||||
def test_execute_and_fetch(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("select * from ACCOUNT")
|
||||
cursor.execute("select ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING " \
|
||||
"from ACCOUNT")
|
||||
result = cursor.fetchall()
|
||||
assert [(u'2009-09-10 14:15:22.123456', 18, 12.4, None),
|
||||
(u'2009-09-11 14:15:22.123456', 19, 12.9, 1)] == result
|
||||
|
||||
def test_execute_and_fetch_parameter(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("select * from ACCOUNT where ACCOUNT_NO = ?", (18,))
|
||||
cursor.execute("select ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING " \
|
||||
"from ACCOUNT where ACCOUNT_NO = ?", (18,))
|
||||
result = cursor.fetchall()
|
||||
assert [(u'2009-09-10 14:15:22.123456', 18, 12.4, None)] == result
|
||||
|
||||
def test_execute_and_fetchone(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("select * from ACCOUNT order by ACCOUNT_NO")
|
||||
cursor.execute("select ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING " \
|
||||
"from ACCOUNT order by ACCOUNT_NO")
|
||||
result = cursor.fetchone()
|
||||
assert (u'2009-09-10 14:15:22.123456', 18, 12.4, None) == result
|
||||
cursor.close()
|
||||
|
|
@ -154,17 +158,19 @@ class IntegrationTest(TestCase):
|
|||
|
||||
def test_execute_and_fetchmany(self):
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("select * from ACCOUNT order by ACCOUNT_NO")
|
||||
cursor.execute("select ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING " \
|
||||
"from ACCOUNT order by ACCOUNT_NO")
|
||||
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()
|
||||
# let this test work with sqlite if __del__ is not overridden
|
||||
# in cursor
|
||||
# cursor.close()
|
||||
|
||||
def test_executemany(self):
|
||||
cursor = self.conn.cursor()
|
||||
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE)" \
|
||||
" values (?, ?, ?)"
|
||||
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE) " \
|
||||
"values (?, ?, ?)"
|
||||
parms = (
|
||||
( '2009-09-11 14:15:22.123450', 20, 13.1 ),
|
||||
( '2009-09-11 14:15:22.123451', 21, 13.2 ),
|
||||
|
|
@ -172,3 +178,46 @@ class IntegrationTest(TestCase):
|
|||
)
|
||||
cursor.executemany(stmt, parms)
|
||||
assert cursor.rowcount == 3
|
||||
|
||||
def test_execute_types(self):
|
||||
cursor = self.conn.cursor()
|
||||
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, " \
|
||||
"BLOCKING, DBL_COL, OPENED_AT, VALID, PRODUCT_NAME) " \
|
||||
"values (?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
d = self.dbapi
|
||||
account_id = d.Timestamp(2010, 01, 26, 14, 31, 59)
|
||||
account_no = 20
|
||||
balance = 1.2
|
||||
blocking = 10.0
|
||||
dbl_col = 3.5
|
||||
opened_at = d.Date(2008, 02, 27)
|
||||
valid = 1
|
||||
product_name = u'Savings account'
|
||||
parms = (account_id, account_no, balance, blocking, dbl_col,
|
||||
opened_at, valid, product_name)
|
||||
cursor.execute(stmt, parms)
|
||||
stmt = "select ACCOUNT_ID, ACCOUNT_NO, BALANCE, BLOCKING, " \
|
||||
"DBL_COL, OPENED_AT, VALID, PRODUCT_NAME " \
|
||||
"from ACCOUNT where ACCOUNT_NO = ?"
|
||||
parms = (20, )
|
||||
cursor.execute(stmt, parms)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
exp = ( '2010-01-26 14:31:59', account_no, balance, blocking,
|
||||
dbl_col, '2008-02-27', valid, product_name )
|
||||
assert exp == result
|
||||
|
||||
def test_execute_type_blob(self):
|
||||
cursor = self.conn.cursor()
|
||||
stmt = "insert into ACCOUNT (ACCOUNT_ID, ACCOUNT_NO, BALANCE, " \
|
||||
"STUFF) values (?, ?, ?, ?)"
|
||||
stuff = self.dbapi.Binary('abcdef')
|
||||
parms = ('2009-09-11 14:15:22.123450', 20, 13.1, stuff)
|
||||
cursor.execute(stmt, parms)
|
||||
stmt = "select STUFF from ACCOUNT where ACCOUNT_NO = ?"
|
||||
parms = (20, )
|
||||
cursor.execute(stmt, parms)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
value = result[0]
|
||||
assert 'abcdef' == value
|
||||
|
|
|
|||
Loading…
Reference in New Issue