Skip to content

Commit de6d596

Browse files
Thin: fixed order in which bind data is sent to the server when LONG and
non-LONG column data is interspersed (#12).
1 parent 7fc65d6 commit de6d596

File tree

6 files changed

+90
-17
lines changed

6 files changed

+90
-17
lines changed

doc/src/release_notes.rst

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,27 @@ oracledb 1.0.1 (TBD)
1818
(`issue 3 <https://github.com/oracle/python-oracledb/issues/3>`__).
1919
#) Thin: return the same value for timestamp with time zone columns as thick
2020
(`issue 7 <https://github.com/oracle/python-oracledb/issues/7>`__).
21-
#) Ensured the name of wrapped functions are the same as the function being
22-
wrapped in order to improve error messages that reference them.
23-
#) Added exception class (oracledb.ConnectionError) as a subclass of
24-
oracledb.DatabaseError in order to aid the handling of connection errors
25-
during creation or use (where the connection is no longer usable or could
26-
not be established).
2721
#) Thin: fixed retry count handling to work in cases where the listener is
2822
running but the service is down
2923
(`issue 3 <https://github.com/oracle/python-oracledb/issues/3>`__).
3024
#) Thin: if an error occurs during the creation of a connection to the
3125
database, the error is wrapped by DPY-6005 as an instance of
3226
oracledb.ConnectionError.
33-
#) Fixed issue where unconstrained numbers containing integer values would be
34-
fetched as floats when oracledb.defaults.fetch_lobs was set to `False`
35-
(`issue 15 <https://github.com/oracle/python-oracledb/issues/15>`__).
27+
#) Thin: fixed order in which bind data is sent to the server when LONG and
28+
non-LONG column data is interspersed
29+
(`issue 12 <https://github.com/oracle/python-oracledb/issues/12>`__).
3630
#) Thin: ensure that errors that occur during fetch are detected consistently.
3731
#) Thin: fixed issue when fetching null values in implicit results.
3832
#) Thin: small optimization when sending column metadata.
33+
#) Fixed issue where unconstrained numbers containing integer values would be
34+
fetched as floats when oracledb.defaults.fetch_lobs was set to `False`
35+
(`issue 15 <https://github.com/oracle/python-oracledb/issues/15>`__).
36+
#) Ensured the name of wrapped functions are the same as the function being
37+
wrapped in order to improve error messages that reference them.
38+
#) Added exception class (oracledb.ConnectionError) as a subclass of
39+
oracledb.DatabaseError in order to aid the handling of connection errors
40+
during creation or use (where the connection is no longer usable or could
41+
not be established).
3942
#) Improved samples and documentation.
4043

4144

src/oracledb/impl/base/types.pyx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,13 @@ DB_TYPE_INTERVAL_DS = DbType(DB_TYPE_NUM_INTERVAL_DS, "DB_TYPE_INTERVAL_DS",
119119
DB_TYPE_INTERVAL_YM = DbType(DB_TYPE_NUM_INTERVAL_YM, "DB_TYPE_INTERVAL_YM",
120120
182)
121121
DB_TYPE_JSON = DbType(DB_TYPE_NUM_JSON, "DB_TYPE_JSON", 119)
122-
DB_TYPE_LONG = DbType(DB_TYPE_NUM_LONG_VARCHAR, "DB_TYPE_LONG", 8, csfrm=1)
122+
DB_TYPE_LONG = DbType(DB_TYPE_NUM_LONG_VARCHAR, "DB_TYPE_LONG", 8, csfrm=1,
123+
buffer_size_factor=2147483647)
123124
DB_TYPE_LONG_NVARCHAR = DbType(DB_TYPE_NUM_LONG_NVARCHAR,
124-
"DB_TYPE_LONG_NVARCHAR", 8, csfrm=2)
125-
DB_TYPE_LONG_RAW = DbType(DB_TYPE_NUM_LONG_RAW, "DB_TYPE_LONG_RAW", 24)
125+
"DB_TYPE_LONG_NVARCHAR", 8, csfrm=2,
126+
buffer_size_factor=2147483647)
127+
DB_TYPE_LONG_RAW = DbType(DB_TYPE_NUM_LONG_RAW, "DB_TYPE_LONG_RAW", 24,
128+
buffer_size_factor=2147483647)
126129
DB_TYPE_NCHAR = DbType(DB_TYPE_NUM_NCHAR, "DB_TYPE_NCHAR", 96, 2000, csfrm=2,
127130
buffer_size_factor=4)
128131
DB_TYPE_NCLOB = DbType(DB_TYPE_NUM_NCLOB, "DB_TYPE_NCLOB", 112, csfrm=2,

src/oracledb/impl/thin/constants.pxi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ DEF TNS_MAX_ROWID_LENGTH = 18
605605
DEF TNS_DURATION_MID = 0x80000000
606606
DEF TNS_DURATION_OFFSET = 60
607607
DEF TNS_DURATION_SESSION = 10
608+
DEF TNS_MIN_LONG_LENGTH = 0x8000
608609
DEF TNS_MAX_LONG_LENGTH = 0x7fffffff
609610
DEF TNS_SDU = 8192
610611
DEF TNS_TDU = 65535

src/oracledb/impl/thin/messages.pyx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,8 +906,7 @@ cdef class MessageWithData(Message):
906906
# expects that and complains if any other value is sent!
907907
buf.write_uint8(0)
908908
buf.write_uint8(0)
909-
if ora_type_num == TNS_DATA_TYPE_LONG \
910-
or ora_type_num == TNS_DATA_TYPE_LONG_RAW:
909+
if var_impl.buffer_size >= TNS_MIN_LONG_LENGTH:
911910
buf.write_ub4(TNS_MAX_LONG_LENGTH)
912911
else:
913912
buf.write_ub4(var_impl.buffer_size)
@@ -999,8 +998,13 @@ cdef class MessageWithData(Message):
999998

1000999
cdef int _write_bind_params_row(self, WriteBuffer buf, list params,
10011000
uint32_t pos) except -1:
1001+
"""
1002+
Write a row of bind parameters. Note that non-LONG values are written
1003+
first followed by any LONG values.
1004+
"""
10021005
cdef:
10031006
uint32_t i, num_elements, offset = self.offset
1007+
bint found_long = False
10041008
ThinVarImpl var_impl
10051009
BindInfo bind_info
10061010
for i, bind_info in enumerate(params):
@@ -1013,6 +1017,18 @@ cdef class MessageWithData(Message):
10131017
for value in var_impl._values[:num_elements]:
10141018
self._write_bind_params_column(buf, var_impl, value)
10151019
else:
1020+
if var_impl.buffer_size >= TNS_MIN_LONG_LENGTH:
1021+
found_long = True
1022+
continue
1023+
self._write_bind_params_column(buf, var_impl,
1024+
var_impl._values[pos + offset])
1025+
if found_long:
1026+
for i, bind_info in enumerate(params):
1027+
if bind_info._is_return_bind:
1028+
continue
1029+
var_impl = bind_info._bind_var_impl
1030+
if var_impl.buffer_size < TNS_MIN_LONG_LENGTH:
1031+
continue
10161032
self._write_bind_params_column(buf, var_impl,
10171033
var_impl._values[pos + offset])
10181034

tests/sql/create_schema.sql

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,19 +166,31 @@ create table &main_user..TestDates (
166166

167167
create table &main_user..TestCLOBs (
168168
IntCol number(9) not null,
169-
CLOBCol clob not null
169+
CLOBCol clob not null,
170+
ExtraNumCol1 number(9),
171+
ExtraCLOBCol1 clob,
172+
ExtraNumCol2 number(9),
173+
ExtraCLOBCol2 clob
170174
)
171175
/
172176

173177
create table &main_user..TestNCLOBs (
174178
IntCol number(9) not null,
175-
NCLOBCol nclob not null
179+
NCLOBCol nclob not null,
180+
ExtraNumCol1 number(9),
181+
ExtraNCLOBCol1 nclob,
182+
ExtraNumCol2 number(9),
183+
ExtraNCLOBCol2 nclob
176184
)
177185
/
178186

179187
create table &main_user..TestBLOBs (
180188
IntCol number(9) not null,
181-
BLOBCol blob not null
189+
BLOBCol blob not null,
190+
ExtraNumCol1 number(9),
191+
ExtraBLOBCol1 blob,
192+
ExtraNumCol2 number(9),
193+
ExtraBLOBCol2 blob
182194
)
183195
/
184196

tests/test_1900_lob_var.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,32 @@ def __perform_test(self, lob_type, input_type):
7878
order by IntCol""")
7979
self.__validate_query(self.cursor, lob_type)
8080

81+
def __test_bind_ordering(self, lob_type):
82+
main_col = "A" * 32768
83+
extra_col_1 = "B" * 65536
84+
extra_col_2 = "C" * 131072
85+
if lob_type == "BLOB":
86+
main_col = main_col.encode()
87+
extra_col_1 = extra_col_1.encode()
88+
extra_col_2 = extra_col_2.encode()
89+
self.connection.stmtcachesize = 0
90+
self.cursor.execute(f"truncate table Test{lob_type}s")
91+
data = (1, main_col, 8, extra_col_1, 15, extra_col_2)
92+
self.cursor.execute(f"""
93+
insert into Test{lob_type}s (
94+
IntCol,
95+
{lob_type}Col,
96+
ExtraNumCol1,
97+
Extra{lob_type}Col1,
98+
ExtraNumCol2,
99+
Extra{lob_type}Col2
100+
) values (:1, :2, :3, :4, :5, :6)""",
101+
data)
102+
with test_env.FetchLobsContextManager(False):
103+
self.cursor.execute(f"select * from Test{lob_type}s")
104+
fetched_data = self.cursor.fetchone()
105+
self.assertEqual(fetched_data, data)
106+
81107
def __test_fetch_lobs_direct(self, lob_type):
82108
self.cursor.execute(f"truncate table Test{lob_type}s")
83109
data = []
@@ -450,5 +476,17 @@ def test_1929_fetch_nclob_as_str(self):
450476
"1929 - test fetching NCLOB as str"
451477
self.__test_fetch_lobs_direct("NCLOB")
452478

479+
def test_1930_bind_order_blob(self):
480+
"1930 - test bind ordering with BLOB"
481+
self.__test_bind_ordering("BLOB")
482+
483+
def test_1931_bind_order_clob(self):
484+
"1931 - test bind ordering with CLOB"
485+
self.__test_bind_ordering("CLOB")
486+
487+
def test_1932_bind_order_nclob(self):
488+
"1932 - test bind ordering with NCLOB"
489+
self.__test_bind_ordering("NCLOB")
490+
453491
if __name__ == "__main__":
454492
test_env.run_test_cases()

0 commit comments

Comments
 (0)