Skip to content

Commit 2c879f8

Browse files
Fixed bug with SQL containing multibyte characters with certain database
character sets (#133).
1 parent 91ee995 commit 2c879f8

File tree

6 files changed

+21
-20
lines changed

6 files changed

+21
-20
lines changed

doc/src/release_notes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Thin Mode Changes
3030
#) Fixed bug with incorrect values of :data:`Cursor.rowcount` when fetching
3131
data
3232
(`issue 147 <https://github.com/oracle/python-oracledb/issues/147>`__).
33+
#) Fixed bug with SQL containing multibyte characters with certain database
34+
character sets
35+
(`issue 133 <https://github.com/oracle/python-oracledb/issues/133>`__).
3336

3437
Thick Mode Changes
3538
++++++++++++++++++

src/oracledb/impl/thin/capabilities.pyx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -38,7 +38,6 @@ cdef class Capabilities:
3838
uint16_t ncharset_id
3939
bytearray compile_caps
4040
bytearray runtime_caps
41-
bint char_conversion
4241
bint supports_oob
4342

4443
def __init__(self):

src/oracledb/impl/thin/connection.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -240,7 +240,7 @@ cdef class ThinConnImpl(BaseConnImpl):
240240
statement = self._statement_cache.get(sql)
241241
if statement is None:
242242
statement = Statement()
243-
statement._prepare(sql, self._protocol._caps.char_conversion)
243+
statement._prepare(sql)
244244
if len(self._statement_cache) < self._statement_cache_size \
245245
and cache_statement \
246246
and not self._drcp_establish_session:

src/oracledb/impl/thin/constants.pxi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ DEF TNS_CHARSET_UTF8 = 873
578578
DEF TNS_CHARSET_UTF16 = 2000
579579
DEF TNS_ENCODING_UTF8 = "UTF-8"
580580
DEF TNS_ENCODING_UTF16 = "UTF-16BE"
581+
DEF TNS_ENCODING_MULTI_BYTE = 0x01
582+
DEF TNS_ENCODING_CONV_LENGTH = 0x02
581583

582584
# compile time capability indices
583585
DEF TNS_CCAP_SQL_VERSION = 0

src/oracledb/impl/thin/messages.pyx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ cdef class MessageWithData(Message):
10901090
buf.write_uint8(1) # pointer
10911091
schema_bytes = self.conn_impl._current_schema.encode()
10921092
buf.write_ub4(len(schema_bytes))
1093-
buf.write_bytes(schema_bytes)
1093+
buf.write_bytes_with_length(schema_bytes)
10941094

10951095
cdef int _write_close_temp_lobs_piggyback(self,
10961096
WriteBuffer buf) except -1:
@@ -1235,15 +1235,15 @@ cdef class MessageWithData(Message):
12351235
# write strings
12361236
if conn._client_identifier_modified \
12371237
and conn._client_identifier is not None:
1238-
buf.write_bytes(client_identifier_bytes)
1238+
buf.write_bytes_with_length(client_identifier_bytes)
12391239
if conn._module_modified and conn._module is not None:
1240-
buf.write_bytes(module_bytes)
1240+
buf.write_bytes_with_length(module_bytes)
12411241
if conn._action_modified and conn._action is not None:
1242-
buf.write_bytes(action_bytes)
1242+
buf.write_bytes_with_length(action_bytes)
12431243
if conn._client_info_modified and conn._client_info is not None:
1244-
buf.write_bytes(client_info_bytes)
1244+
buf.write_bytes_with_length(client_info_bytes)
12451245
if conn._dbop_modified and conn._dbop is not None:
1246-
buf.write_bytes(dbop_bytes)
1246+
buf.write_bytes_with_length(dbop_bytes)
12471247

12481248
# reset flags and values
12491249
conn._action_modified = False
@@ -1548,7 +1548,7 @@ cdef class AuthMessage(Message):
15481548
buf.write_uint8(1) # pointer (authovl)
15491549
buf.write_uint8(1) # pointer (authovln)
15501550
if has_user:
1551-
buf.write_bytes(self.user_bytes)
1551+
buf.write_bytes_with_length(self.user_bytes)
15521552

15531553
# write key/value pairs
15541554
if self.function_code == TNS_FUNC_AUTH_PHASE_ONE:
@@ -1712,7 +1712,8 @@ cdef class DataTypesMessage(Message):
17121712
buf.write_uint8(TNS_MSG_TYPE_DATA_TYPES)
17131713
buf.write_uint16(TNS_CHARSET_UTF8, BYTE_ORDER_LSB)
17141714
buf.write_uint16(TNS_CHARSET_UTF8, BYTE_ORDER_LSB)
1715-
buf.write_ub4(len(buf._caps.compile_caps))
1715+
buf.write_uint8(TNS_ENCODING_MULTI_BYTE | TNS_ENCODING_CONV_LENGTH)
1716+
buf.write_uint8(len(buf._caps.compile_caps))
17161717
buf.write_bytes(bytes(buf._caps.compile_caps))
17171718
buf.write_uint8(len(buf._caps.runtime_caps))
17181719
buf.write_bytes(bytes(buf._caps.runtime_caps))
@@ -1867,7 +1868,7 @@ cdef class ExecuteMessage(MessageWithData):
18671868
if stmt._cursor_id == 0 or stmt._is_ddl:
18681869
if stmt._sql_bytes is None:
18691870
errors._raise_err(errors.ERR_INVALID_REF_CURSOR)
1870-
buf.write_bytes(stmt._sql_bytes)
1871+
buf.write_bytes_with_length(stmt._sql_bytes)
18711872
buf.write_ub4(1) # al8i4[0] parse
18721873
else:
18731874
buf.write_ub4(0) # al8i4[0] parse
@@ -2133,7 +2134,6 @@ cdef class ProtocolMessage(Message):
21332134
if c == 0:
21342135
break
21352136
buf.read_uint16(&caps.charset_id, BYTE_ORDER_LSB)
2136-
buf._caps.char_conversion = caps.charset_id != TNS_CHARSET_UTF8
21372137
buf.skip_ub1() # skip server flags
21382138
buf.read_uint16(&num_elem, BYTE_ORDER_LSB)
21392139
if num_elem > 0: # skip elements

src/oracledb/impl/thin/statement.pyx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -156,7 +156,7 @@ cdef class Statement:
156156
elif sql_keyword in ("CREATE", "ALTER", "DROP", "TRUNCATE"):
157157
self._is_ddl = True
158158

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

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

0 commit comments

Comments
 (0)