Skip to content

Commit 37144e0

Browse files
Fixed bug when fetching nested cursors.
1 parent a622afa commit 37144e0

File tree

6 files changed

+89
-3
lines changed

6 files changed

+89
-3
lines changed

doc/src/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Thin Mode Changes
3838
#) Fixed bug when connecting to an AC-enabled service
3939
(`issue 476 <https://github.com/oracle/python-oracledb/issues/476>`__).
4040
#) Fixed bug when using temporary LOBs with implicit pooling.
41+
#) Fixed bug when fetching nested cursors.
4142

4243
Thick Mode Changes
4344
++++++++++++++++++

src/oracledb/impl/thin/messages/base.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,8 @@ cdef class MessageWithData(Message):
896896
column_value = self._create_cursor_from_describe(buf, column_value)
897897
cursor_impl = column_value._impl
898898
buf.read_ub2(&cursor_impl._statement._cursor_id)
899+
if self.in_fetch:
900+
cursor_impl._statement._is_nested = True
899901
elif ora_type_num in (ORA_TYPE_NUM_CLOB,
900902
ORA_TYPE_NUM_BLOB,
901903
ORA_TYPE_NUM_BFILE):

src/oracledb/impl/thin/statement.pyx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2024, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2025, 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
@@ -305,6 +305,7 @@ cdef class Statement:
305305
bint _no_prefetch
306306
bint _requires_define
307307
bint _return_to_cache
308+
bint _is_nested
308309
bint _in_use
309310

310311
cdef Statement copy(self):

src/oracledb/impl/thin/statement_cache.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2024, Oracle and/or its affiliates.
2+
# Copyright (c) 2024, 2025, 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
@@ -45,7 +45,7 @@ cdef class StatementCache:
4545
Add the statement's cursor to the list of cursors that need to be
4646
closed.
4747
"""
48-
if stmt._cursor_id != 0:
48+
if stmt._cursor_id != 0 and not stmt._is_nested:
4949
self._cursors_to_close[self._num_cursors_to_close] = \
5050
stmt._cursor_id
5151
self._num_cursors_to_close += 1

tests/test_1300_cursor_var.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,47 @@ def test_1319(self):
459459
with self.assertRaisesFullCode("DPY-3027"):
460460
self.cursor.execute(sql, [ref_cursor])
461461

462+
def test_1320(self):
463+
"1320 - test fetching nested cursors repeatedly"
464+
sql = """
465+
select
466+
s.Description,
467+
cursor(select 'Nested String for ' || s.Description from dual)
468+
from
469+
(
470+
select 'Top Level String 1' as Description
471+
from dual
472+
union all
473+
select 'Top Level String 2'
474+
from dual
475+
union all
476+
select 'Top Level String 3'
477+
from dual
478+
union all
479+
select 'Top Level String 4'
480+
from dual
481+
union all
482+
select 'Top Level String 5'
483+
from dual
484+
) s"""
485+
486+
for i in range(3):
487+
with self.conn.cursor() as cursor:
488+
cursor.arraysize = 10
489+
cursor.execute(sql)
490+
desc, nested1 = cursor.fetchone()
491+
self.assertEqual(desc, "Top Level String 1")
492+
nested_rows = nested1.fetchall()
493+
self.assertEqual(
494+
nested_rows, [("Nested String for Top Level String 1",)]
495+
)
496+
desc, nested2 = cursor.fetchone()
497+
self.assertEqual(desc, "Top Level String 2")
498+
nested_rows = nested2.fetchall()
499+
self.assertEqual(
500+
nested_rows, [("Nested String for Top Level String 2",)]
501+
)
502+
462503

463504
if __name__ == "__main__":
464505
test_env.run_test_cases()

tests/test_6300_cursor_other_async.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,47 @@ async def test_6351(self):
872872
await self.cursor.execute("select * from TestJsonCols order by IntCol")
873873
self.assertEqual(await self.cursor.fetchall(), expected_data)
874874

875+
async def test_6352(self):
876+
"6352 - test fetching nested cursors repeatedly"
877+
sql = """
878+
select
879+
s.Description,
880+
cursor(select 'Nested String for ' || s.Description from dual)
881+
from
882+
(
883+
select 'Top Level String 1' as Description
884+
from dual
885+
union all
886+
select 'Top Level String 2'
887+
from dual
888+
union all
889+
select 'Top Level String 3'
890+
from dual
891+
union all
892+
select 'Top Level String 4'
893+
from dual
894+
union all
895+
select 'Top Level String 5'
896+
from dual
897+
) s"""
898+
899+
for i in range(3):
900+
with self.conn.cursor() as cursor:
901+
cursor.arraysize = 10
902+
await cursor.execute(sql)
903+
desc, nested1 = await cursor.fetchone()
904+
self.assertEqual(desc, "Top Level String 1")
905+
nested_rows = await nested1.fetchall()
906+
self.assertEqual(
907+
nested_rows, [("Nested String for Top Level String 1",)]
908+
)
909+
desc, nested2 = await cursor.fetchone()
910+
self.assertEqual(desc, "Top Level String 2")
911+
nested_rows = await nested2.fetchall()
912+
self.assertEqual(
913+
nested_rows, [("Nested String for Top Level String 2",)]
914+
)
915+
875916

876917
if __name__ == "__main__":
877918
test_env.run_test_cases()

0 commit comments

Comments
 (0)