Skip to content

Commit

Permalink
Fixed bug with SQL containing multibyte characters with certain database
Browse files Browse the repository at this point in the history
character sets (#133).
  • Loading branch information
anthony-tuininga committed Mar 9, 2023
1 parent 91ee995 commit 2c879f8
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 20 deletions.
3 changes: 3 additions & 0 deletions doc/src/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ Thin Mode Changes
#) Fixed bug with incorrect values of :data:`Cursor.rowcount` when fetching
data
(`issue 147 <https://github.com/oracle/python-oracledb/issues/147>`__).
#) Fixed bug with SQL containing multibyte characters with certain database
character sets
(`issue 133 <https://github.com/oracle/python-oracledb/issues/133>`__).

Thick Mode Changes
++++++++++++++++++
Expand Down
3 changes: 1 addition & 2 deletions src/oracledb/impl/thin/capabilities.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
Expand Down Expand Up @@ -38,7 +38,6 @@ cdef class Capabilities:
uint16_t ncharset_id
bytearray compile_caps
bytearray runtime_caps
bint char_conversion
bint supports_oob

def __init__(self):
Expand Down
4 changes: 2 additions & 2 deletions src/oracledb/impl/thin/connection.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
Expand Down Expand Up @@ -240,7 +240,7 @@ cdef class ThinConnImpl(BaseConnImpl):
statement = self._statement_cache.get(sql)
if statement is None:
statement = Statement()
statement._prepare(sql, self._protocol._caps.char_conversion)
statement._prepare(sql)
if len(self._statement_cache) < self._statement_cache_size \
and cache_statement \
and not self._drcp_establish_session:
Expand Down
2 changes: 2 additions & 0 deletions src/oracledb/impl/thin/constants.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,8 @@ DEF TNS_CHARSET_UTF8 = 873
DEF TNS_CHARSET_UTF16 = 2000
DEF TNS_ENCODING_UTF8 = "UTF-8"
DEF TNS_ENCODING_UTF16 = "UTF-16BE"
DEF TNS_ENCODING_MULTI_BYTE = 0x01
DEF TNS_ENCODING_CONV_LENGTH = 0x02

# compile time capability indices
DEF TNS_CCAP_SQL_VERSION = 0
Expand Down
20 changes: 10 additions & 10 deletions src/oracledb/impl/thin/messages.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ cdef class MessageWithData(Message):
buf.write_uint8(1) # pointer
schema_bytes = self.conn_impl._current_schema.encode()
buf.write_ub4(len(schema_bytes))
buf.write_bytes(schema_bytes)
buf.write_bytes_with_length(schema_bytes)

cdef int _write_close_temp_lobs_piggyback(self,
WriteBuffer buf) except -1:
Expand Down Expand Up @@ -1235,15 +1235,15 @@ cdef class MessageWithData(Message):
# write strings
if conn._client_identifier_modified \
and conn._client_identifier is not None:
buf.write_bytes(client_identifier_bytes)
buf.write_bytes_with_length(client_identifier_bytes)
if conn._module_modified and conn._module is not None:
buf.write_bytes(module_bytes)
buf.write_bytes_with_length(module_bytes)
if conn._action_modified and conn._action is not None:
buf.write_bytes(action_bytes)
buf.write_bytes_with_length(action_bytes)
if conn._client_info_modified and conn._client_info is not None:
buf.write_bytes(client_info_bytes)
buf.write_bytes_with_length(client_info_bytes)
if conn._dbop_modified and conn._dbop is not None:
buf.write_bytes(dbop_bytes)
buf.write_bytes_with_length(dbop_bytes)

# reset flags and values
conn._action_modified = False
Expand Down Expand Up @@ -1548,7 +1548,7 @@ cdef class AuthMessage(Message):
buf.write_uint8(1) # pointer (authovl)
buf.write_uint8(1) # pointer (authovln)
if has_user:
buf.write_bytes(self.user_bytes)
buf.write_bytes_with_length(self.user_bytes)

# write key/value pairs
if self.function_code == TNS_FUNC_AUTH_PHASE_ONE:
Expand Down Expand Up @@ -1712,7 +1712,8 @@ cdef class DataTypesMessage(Message):
buf.write_uint8(TNS_MSG_TYPE_DATA_TYPES)
buf.write_uint16(TNS_CHARSET_UTF8, BYTE_ORDER_LSB)
buf.write_uint16(TNS_CHARSET_UTF8, BYTE_ORDER_LSB)
buf.write_ub4(len(buf._caps.compile_caps))
buf.write_uint8(TNS_ENCODING_MULTI_BYTE | TNS_ENCODING_CONV_LENGTH)
buf.write_uint8(len(buf._caps.compile_caps))
buf.write_bytes(bytes(buf._caps.compile_caps))
buf.write_uint8(len(buf._caps.runtime_caps))
buf.write_bytes(bytes(buf._caps.runtime_caps))
Expand Down Expand Up @@ -1867,7 +1868,7 @@ cdef class ExecuteMessage(MessageWithData):
if stmt._cursor_id == 0 or stmt._is_ddl:
if stmt._sql_bytes is None:
errors._raise_err(errors.ERR_INVALID_REF_CURSOR)
buf.write_bytes(stmt._sql_bytes)
buf.write_bytes_with_length(stmt._sql_bytes)
buf.write_ub4(1) # al8i4[0] parse
else:
buf.write_ub4(0) # al8i4[0] parse
Expand Down Expand Up @@ -2133,7 +2134,6 @@ cdef class ProtocolMessage(Message):
if c == 0:
break
buf.read_uint16(&caps.charset_id, BYTE_ORDER_LSB)
buf._caps.char_conversion = caps.charset_id != TNS_CHARSET_UTF8
buf.skip_ub1() # skip server flags
buf.read_uint16(&num_elem, BYTE_ORDER_LSB)
if num_elem > 0: # skip elements
Expand Down
9 changes: 3 additions & 6 deletions src/oracledb/impl/thin/statement.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#------------------------------------------------------------------------------
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
#
# This software is dual-licensed to you under the Universal Permissive License
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
Expand Down Expand Up @@ -156,7 +156,7 @@ cdef class Statement:
elif sql_keyword in ("CREATE", "ALTER", "DROP", "TRUNCATE"):
self._is_ddl = True

cdef int _prepare(self, str sql, bint char_conversion) except -1:
cdef int _prepare(self, str sql) except -1:
"""
Prepare the SQL for execution by determining the list of bind names
that are found within it. The length of the SQL text is also calculated
Expand All @@ -171,10 +171,7 @@ cdef class Statement:
# retain normalized SQL (as string and bytes) as well as the length
self._sql = sql
self._sql_bytes = self._sql.encode()
if char_conversion:
self._sql_length = <uint32_t> len(self._sql)
else:
self._sql_length = <uint32_t> len(self._sql_bytes)
self._sql_length = <uint32_t> len(self._sql_bytes)

# create empty list (bind by position) and dict (bind by name)
self._bind_info_dict = collections.OrderedDict()
Expand Down

0 comments on commit 2c879f8

Please sign in to comment.