Fixed bug with binding boolean values when using Oracle Database 23c

(#263).
This commit is contained in:
Anthony Tuininga 2023-12-09 12:08:43 -07:00
parent 86032f96ba
commit 72bb4e4d79
12 changed files with 53 additions and 44 deletions

View File

@ -69,6 +69,8 @@ Common Changes
and scale allow for it -- in the same way that numbers are fetched from the
database
(`issue 99 <https://github.com/oracle/python-oracledb/issues/99>`__).
#) Fixed bug with binding boolean values with Oracle Database 23c
(`issue 263 <https://github.com/oracle/python-oracledb/issues/263>`__).
#) Fixed bug with getting unknown attributes from DbObject instances.
#) Error ``DPY-4029: errors in array DML exceed 65535`` is now raised when the
number of batch errors exceeds 65535 when calling

View File

@ -243,6 +243,8 @@ cdef class BaseConnImpl:
public object outputtypehandler
public bint autocommit
public bint invoke_session_callback
readonly tuple server_version
readonly bint supports_bool
cdef object _check_value(self, DbType dbtype, BaseDbObjectTypeImpl objtype,
object value, bint* is_ok)

View File

@ -1081,7 +1081,7 @@ class Connection:
"""
if self._version is None:
self._verify_connected()
self._version = self._impl.get_version()
self._version = ".".join(str(c) for c in self._impl.server_version)
return self._version
def xid(

View File

@ -264,10 +264,6 @@ cdef class BaseConnImpl:
def get_type(self, object conn, str name):
pass
@utils.CheckImpls("getting the database version")
def get_version(self):
pass
@utils.CheckImpls("pinging the database")
def ping(self):
pass

View File

@ -220,7 +220,9 @@ cdef class BaseVarImpl:
dbtype = DB_TYPE_VARCHAR
size = 1
elif isinstance(value, PY_TYPE_BOOL):
dbtype = DB_TYPE_BOOLEAN if is_plsql else DB_TYPE_BINARY_INTEGER
dbtype = DB_TYPE_BOOLEAN \
if self._conn_impl.supports_bool or is_plsql \
else DB_TYPE_BINARY_INTEGER
elif isinstance(value, str):
size = <uint32_t> len(value)
dbtype = DB_TYPE_VARCHAR

View File

@ -270,6 +270,7 @@ cdef class ThickConnImpl(BaseConnImpl):
dpiConnCreateParams conn_params
ConnectParamsImpl pool_params
dpiAccessToken access_token
dpiVersionInfo version_info
ConnectionParams params
int status
@ -387,8 +388,20 @@ cdef class ThickConnImpl(BaseConnImpl):
params.password_len, params.dsn_ptr,
params.dsn_len, &common_params,
&conn_params, &self._handle)
if status == DPI_SUCCESS:
status = dpiConn_getServerVersion(self._handle, NULL, NULL,
&version_info)
if status < 0:
_raise_from_odpi()
self.server_version = (
version_info.versionNum,
version_info.releaseNum,
version_info.updateNum,
version_info.portReleaseNum,
version_info.portUpdateNum
)
self.supports_bool = client_version_info.versionNum >= 23 \
and version_info.versionNum >= 23
# determine if session callback should be invoked; this takes place if
# the connection is newly created by the pool or if the requested tag
@ -581,20 +594,6 @@ cdef class ThickConnImpl(BaseConnImpl):
finally:
dpiObjectType_release(handle)
def get_version(self):
cdef:
dpiVersionInfo version_info
int status
with nogil:
status = dpiConn_getServerVersion(self._handle, NULL, NULL,
&version_info)
if status < 0:
_raise_from_odpi()
return "%d.%d.%d.%d.%d" % \
(version_info.versionNum, version_info.releaseNum,
version_info.updateNum, version_info.portReleaseNum,
version_info.portUpdateNum)
def set_action(self, str value):
self._set_text_attr(dpiConn_setAction, value)

View File

@ -433,21 +433,15 @@ def clientversion():
The five values are the major version, minor version, update number, patch
number and port update number.
"""
cdef dpiVersionInfo info
global client_version
if client_version is None:
if driver_context == NULL:
errors._raise_err(errors.ERR_INIT_ORACLE_CLIENT_NOT_CALLED)
if dpiContext_getClientVersion(driver_context, &info) < 0:
_raise_from_odpi()
client_version = (
info.versionNum,
info.releaseNum,
info.updateNum,
info.portReleaseNum,
info.portUpdateNum
)
return client_version
if driver_context == NULL:
errors._raise_err(errors.ERR_INIT_ORACLE_CLIENT_NOT_CALLED)
return (
client_version_info.versionNum,
client_version_info.releaseNum,
client_version_info.updateNum,
client_version_info.portReleaseNum,
client_version_info.portUpdateNum
)
def init_oracle_client(lib_dir=None, config_dir=None, error_url=None,
@ -494,6 +488,9 @@ def init_oracle_client(lib_dir=None, config_dir=None, error_url=None,
&params, &driver_context,
&error_info) < 0:
_raise_from_info(&error_info)
if dpiContext_getClientVersion(driver_context,
&client_version_info) < 0:
_raise_from_odpi()
driver_context_params = params_tuple

View File

@ -36,7 +36,6 @@ cdef class ThinConnImpl(BaseConnImpl):
uint32_t _statement_cache_size
object _statement_cache_lock
Protocol _protocol
str _server_version
uint32_t _session_id
uint32_t _serial_num
str _action
@ -353,9 +352,6 @@ cdef class ThinConnImpl(BaseConnImpl):
get_dbobject_type_cache(self._dbobject_type_cache_num)
return cache.get_type(conn, name)
def get_version(self):
return self._server_version
def ping(self):
cdef Message message
message = self._create_message(PingMessage)

View File

@ -1538,9 +1538,9 @@ cdef class AuthMessage(Message):
self.session_data.get("AUTH_SC_SERVICE_NAME")
self.conn_impl._instance_name = \
self.session_data.get("AUTH_INSTANCENAME")
self.conn_impl._server_version = \
"%d.%d.%d.%d.%d" % self._get_version_tuple(buf)
self.conn_impl.server_version = self._get_version_tuple(buf)
self.conn_impl.supports_bool = \
buf._caps.ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1
cdef int _set_params(self, ConnectParamsImpl params,
Description description) except -1:

View File

@ -67,9 +67,9 @@ cdef type PY_TYPE_LOB
cdef type PY_TYPE_TIMEDELTA = datetime.timedelta
cdef dpiContext *driver_context = NULL
cdef dpiVersionInfo client_version_info
driver_context_params = None
client_version = None
include "impl/thick/buffer.pyx"
include "impl/thick/connection.pyx"

View File

@ -106,6 +106,21 @@ class TestCase(test_env.BaseTestCase):
)
self.assertIsNone(result)
@unittest.skipUnless(
test_env.get_client_version() >= (23, 1), "unsupported client"
)
@unittest.skipUnless(
test_env.get_server_version() >= (23, 1), "unsupported server"
)
def test_3109_bind_and_fetch_boolean_23c(self):
"3109 - test binding and fetching boolean with 23c"
for value in (True, False):
with self.subTest(value=value):
self.cursor.execute("select not :1 from dual", [value])
(fetched_value,) = self.cursor.fetchone()
self.assertIsInstance(fetched_value, bool)
self.assertEqual(fetched_value, not value)
if __name__ == "__main__":
test_env.run_test_cases()

View File

@ -1079,7 +1079,7 @@ class Connection:
"""
if self._version is None:
self._verify_connected()
self._version = self._impl.get_version()
self._version = ".".join(str(c) for c in self._impl.server_version)
return self._version
def xid(