Skip to content

Commit 435fb8a

Browse files
Fixed bug when the fetched data type of a column changes from
oracledb.DB_TYPE_LONG or oracledb.DB_TYPE_LONG_RAW to to a different compatible type (#424).
1 parent 65536ad commit 435fb8a

File tree

3 files changed

+154
-23
lines changed

3 files changed

+154
-23
lines changed

doc/src/release_notes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ of python-oracledb), as affecting the optional :ref:`Thick Mode
1414
oracledb 2.5.1 (TBD)
1515
--------------------
1616

17+
Thin Mode Changes
18+
+++++++++++++++++
19+
20+
#) Fixed bug when the fetched data type of a column changes from
21+
:data:`oracledb.DB_TYPE_LONG` or :data:`oracledb.DB_TYPE_LONG_RAW` to
22+
to a different compatible type
23+
(`issue 424 <https://github.com/oracle/python-oracledb/issues/424>`__).
24+
1725
Thick Mode Changes
1826
++++++++++++++++++
1927

@@ -24,6 +32,7 @@ Thick Mode Changes
2432
older) to connect to Oracle Database 12.2, or later.
2533
(`ODPI-C <https://github.com/oracle/odpi>`__ dependency update).
2634

35+
2736
oracledb 2.5.0 (November 2024)
2837
------------------------------
2938

src/oracledb/impl/thin/messages.pyx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -375,21 +375,31 @@ cdef class MessageWithData(Message):
375375
"""
376376
cdef:
377377
FetchInfoImpl prev_fetch_info = prev_var_impl._fetch_info
378-
uint8_t csfrm = prev_var_impl.dbtype._csfrm
379-
uint8_t type_num
380-
if fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_CLOB \
378+
uint8_t type_num, csfrm
379+
if fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_VARCHAR \
380+
and prev_fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_LONG:
381+
type_num = TNS_DATA_TYPE_LONG
382+
csfrm = fetch_info.dbtype._csfrm
383+
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num, csfrm)
384+
385+
elif fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_RAW \
386+
and prev_fetch_info.dbtype._ora_type_num == \
387+
TNS_DATA_TYPE_LONG_RAW:
388+
type_num = TNS_DATA_TYPE_LONG_RAW
389+
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num, 0)
390+
elif fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_CLOB \
381391
and prev_fetch_info.dbtype._ora_type_num in \
382392
(TNS_DATA_TYPE_CHAR, TNS_DATA_TYPE_VARCHAR,
383393
TNS_DATA_TYPE_LONG):
384394
type_num = TNS_DATA_TYPE_LONG
395+
csfrm = prev_var_impl.dbtype._csfrm
385396
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num,
386397
csfrm)
387398
elif fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_BLOB \
388399
and prev_fetch_info.dbtype._ora_type_num in \
389400
(TNS_DATA_TYPE_RAW, TNS_DATA_TYPE_LONG_RAW):
390401
type_num = TNS_DATA_TYPE_LONG_RAW
391-
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num,
392-
csfrm)
402+
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num, 0)
393403

394404
cdef object _create_cursor_from_describe(self, ReadBuffer buf,
395405
object cursor=None):

tests/test_4600_type_changes.py

Lines changed: 130 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import datetime
3030
import unittest
3131

32+
import oracledb
3233
import test_env
3334

3435

@@ -40,27 +41,33 @@ def __test_type_change(
4041
query_frag_2,
4142
query_value_2,
4243
table_name="dual",
44+
type_handler=None,
4345
):
4446
if test_env.get_is_implicit_pooling():
4547
self.skipTest("sessions can change with implicit pooling")
46-
self.cursor.execute(
47-
f"""
48-
create or replace view TestTypesChanged as
49-
select {query_frag_1} as value
50-
from {table_name}
51-
"""
52-
)
53-
self.cursor.execute("select * from TestTypesChanged")
54-
self.assertEqual(self.cursor.fetchall(), [(query_value_1,)])
55-
self.cursor.execute(
56-
f"""
57-
create or replace view TestTypesChanged as
58-
select {query_frag_2} as value
59-
from dual
60-
"""
61-
)
62-
self.cursor.execute("select * from TestTypesChanged")
63-
self.assertEqual(self.cursor.fetchall(), [(query_value_2,)])
48+
orig_type_handler = self.conn.outputtypehandler
49+
self.conn.outputtypehandler = type_handler
50+
try:
51+
self.cursor.execute(
52+
f"""
53+
create or replace view TestTypesChanged as
54+
select {query_frag_1} as value
55+
from {table_name}
56+
"""
57+
)
58+
self.cursor.execute("select * from TestTypesChanged")
59+
self.assertEqual(self.cursor.fetchall(), [(query_value_1,)])
60+
self.cursor.execute(
61+
f"""
62+
create or replace view TestTypesChanged as
63+
select {query_frag_2} as value
64+
from dual
65+
"""
66+
)
67+
self.cursor.execute("select * from TestTypesChanged")
68+
self.assertEqual(self.cursor.fetchall(), [(query_value_2,)])
69+
finally:
70+
self.conn.outputtypehandler = orig_type_handler
6471

6572
@unittest.skipIf(
6673
not test_env.get_is_thin(),
@@ -273,6 +280,111 @@ def test_4616(self):
273280
datetime.datetime(2022, 1, 5, 0, 0),
274281
)
275282

283+
@unittest.skipIf(
284+
not test_env.get_is_thin(),
285+
"thick mode doesn't support this type change",
286+
)
287+
def test_4617(self):
288+
"4617 - test data type changing from CLOB to VARCHAR"
289+
290+
def type_handler(cursor, metadata):
291+
if metadata.type_code is oracledb.DB_TYPE_CLOB:
292+
return cursor.var(
293+
oracledb.DB_TYPE_VARCHAR,
294+
size=32768,
295+
arraysize=cursor.arraysize,
296+
)
297+
298+
self.__test_type_change(
299+
"to_clob('clob_4617')",
300+
"clob_4617",
301+
"cast('string_4617' as VARCHAR2(15))",
302+
"string_4617",
303+
type_handler=type_handler,
304+
)
305+
306+
@unittest.skipIf(
307+
not test_env.get_is_thin(),
308+
"thick mode doesn't support this type change",
309+
)
310+
def test_4618(self):
311+
"4618 - test data type changing from NCLOB to NVARCHAR"
312+
313+
def type_handler(cursor, metadata):
314+
if metadata.type_code is oracledb.DB_TYPE_NCLOB:
315+
return cursor.var(
316+
oracledb.DB_TYPE_NVARCHAR,
317+
size=32768,
318+
arraysize=cursor.arraysize,
319+
)
320+
321+
self.__test_type_change(
322+
"to_nclob('nclob_4618')",
323+
"nclob_4618",
324+
"cast('nstring_4618' as NVARCHAR2(15))",
325+
"nstring_4618",
326+
type_handler=type_handler,
327+
)
328+
329+
@unittest.skipIf(
330+
not test_env.get_is_thin(),
331+
"thick mode doesn't support this type change",
332+
)
333+
def test_4619(self):
334+
"4619 - test data type changing from CLOB to NVARCHAR"
335+
336+
def type_handler(cursor, metadata):
337+
if metadata.type_code is oracledb.DB_TYPE_CLOB:
338+
return cursor.var(
339+
oracledb.DB_TYPE_NVARCHAR,
340+
size=32768,
341+
arraysize=cursor.arraysize,
342+
)
343+
344+
self.__test_type_change(
345+
"to_clob('clob_4619')",
346+
"clob_4619",
347+
"cast('string_4619' as VARCHAR2(15))",
348+
"string_4619",
349+
type_handler=type_handler,
350+
)
351+
352+
@unittest.skipIf(
353+
not test_env.get_is_thin(),
354+
"thick mode doesn't support this type change",
355+
)
356+
def test_4620(self):
357+
"4620 - test data type changing from BLOB to RAW"
358+
359+
def type_handler(cursor, metadata):
360+
if metadata.type_code is oracledb.DB_TYPE_BLOB:
361+
return cursor.var(
362+
oracledb.DB_TYPE_RAW,
363+
size=32768,
364+
arraysize=cursor.arraysize,
365+
)
366+
367+
self.__test_type_change(
368+
"to_blob(utl_raw.cast_to_raw('blob_4620'))",
369+
b"blob_4620",
370+
"utl_raw.cast_to_raw('string_4620')",
371+
b"string_4620",
372+
type_handler=type_handler,
373+
)
374+
375+
@unittest.skipIf(
376+
not test_env.get_is_thin(),
377+
"thick mode doesn't support this type change",
378+
)
379+
def test_4621(self):
380+
"4621 - test data type changing from NVARCHAR to CLOB"
381+
self.__test_type_change(
382+
"cast('string_4621' as NVARCHAR2(15))",
383+
"string_4621",
384+
"to_clob('clob_4621')",
385+
"clob_4621",
386+
)
387+
276388

277389
if __name__ == "__main__":
278390
test_env.run_test_cases()

0 commit comments

Comments
 (0)