Skip to content

Commit 7e7133b

Browse files
The value of prefetchrows for REF CURSOR variables is now honored
(#482).
1 parent c8babe7 commit 7e7133b

File tree

6 files changed

+114
-6
lines changed

6 files changed

+114
-6
lines changed

doc/src/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Version 8.1 (TBD)
2323
#) The ability to pickle/unpickle Database and API types has been restored.
2424
#) Tests can now be run with tox in order to automate testing of the different
2525
environments that are supported.
26+
#) The value of prefetchrows for REF CURSOR variables is now honored.
2627
#) Improved documentation and test suite.
2728

2829

samples/RefCursor.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,43 @@
1313
connection = cx_Oracle.connect(SampleEnv.GetMainConnectString())
1414
cursor = connection.cursor()
1515

16-
refCursor = connection.cursor()
17-
cursor.callproc("myrefcursorproc", (2, 6, refCursor))
16+
ref_cursor = connection.cursor()
17+
cursor.callproc("myrefcursorproc", (2, 6, ref_cursor))
1818
print("Rows between 2 and 6:")
19-
for row in refCursor:
19+
for row in ref_cursor:
2020
print(row)
2121
print()
2222

23-
refCursor = connection.cursor()
24-
cursor.callproc("myrefcursorproc", (8, 9, refCursor))
23+
ref_cursor = connection.cursor()
24+
cursor.callproc("myrefcursorproc", (8, 9, ref_cursor))
2525
print("Rows between 8 and 9:")
26-
for row in refCursor:
26+
for row in ref_cursor:
2727
print(row)
2828
print()
2929

30+
#------------------------------------------------------------------------------
31+
# Setting prefetchrows and arraysize of a REF cursor can improve performance
32+
# when fetching a large number of rows (Tuned Fetch)
33+
#------------------------------------------------------------------------------
34+
35+
# Truncate the table used for this demo
36+
cursor.execute("truncate table TestTempTable")
37+
38+
# Populate the table with a large number of rows
39+
num_rows = 50000
40+
sql = "insert into TestTempTable (IntCol) values (:1)"
41+
data = [(n + 1,) for n in range(num_rows)]
42+
cursor.executemany(sql, data)
43+
44+
# Set the arraysize and prefetch rows of the REF cursor
45+
ref_cursor = connection.cursor()
46+
ref_cursor.prefetchrows = 1000
47+
ref_cursor.arraysize = 1000
48+
49+
# Perform the tuned fetch
50+
sum_rows = 0
51+
cursor.callproc("myrefcursorproc2", [ref_cursor])
52+
print("Sum of IntCol for", num_rows, "rows:")
53+
for row in ref_cursor:
54+
sum_rows += row[0]
55+
print(sum_rows)

samples/sql/SetupSamplesExec.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,15 @@ begin
382382
end;
383383
/
384384

385+
create procedure &main_user..myrefcursorproc2 (
386+
a_RefCursor out sys_refcursor
387+
) as
388+
begin
389+
open a_RefCursor for
390+
select *
391+
from TestTempTable;
392+
end;
393+
/
385394

386395
--
387396
-- Create package for demoing PL/SQL collections and records.

src/cxoVar.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ static int cxoVar_setValueCursor(cxoVar *var, uint32_t pos, dpiData *data,
462462
cursor->handle = data->value.asStmt;
463463
dpiStmt_addRef(cursor->handle);
464464
}
465+
466+
if (dpiStmt_setPrefetchRows(cursor->handle, cursor->prefetchRows) < 0)
467+
return cxoError_raiseAndReturnInt();
468+
465469
cursor->fixupRefCursor = 1;
466470
return 0;
467471
}

test/sql/SetupTestExec.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,17 @@ begin
443443
end;
444444
/
445445

446+
-- create procedure for testing refcursor
447+
create procedure &main_user..myrefcursorproc (
448+
a_RefCursor out sys_refcursor
449+
) as
450+
begin
451+
open a_RefCursor for
452+
select *
453+
from TestTempTable;
454+
end;
455+
/
456+
446457
-- create functions for testing callfunc()
447458
create function &main_user..func_Test (
448459
a_String varchar2,

test/test_1200_cursor.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,5 +791,62 @@ def test_1265_prefetchrows(self):
791791
cursor.execute("select IntCol from TestTempTable").fetchall()
792792
self.assertRoundTrips(2)
793793

794+
def test_1266_refcursor_prefetchrows(self):
795+
"1266 - test prefetch rows and arraysize using a refcursor"
796+
self.setup_round_trip_checker()
797+
798+
# simple DDL only requires a single round trip
799+
with self.connection.cursor() as cursor:
800+
cursor.execute("truncate table TestTempTable")
801+
self.assertRoundTrips(1)
802+
803+
# array execution only requires a single round trip
804+
num_rows = 590
805+
with self.connection.cursor() as cursor:
806+
sql = "insert into TestTempTable (IntCol) values (:1)"
807+
data = [(n + 1,) for n in range(num_rows)]
808+
cursor.executemany(sql, data)
809+
self.assertRoundTrips(1)
810+
811+
# create refcursor and execute stored procedure
812+
with self.connection.cursor() as cursor:
813+
refcursor = self.connection.cursor()
814+
refcursor.prefetchrows = 300
815+
refcursor.arraysize = 300
816+
cursor.callproc("myrefcursorproc", [refcursor])
817+
refcursor.fetchall()
818+
self.assertRoundTrips(2)
819+
820+
def test_1267_existing_cursor_prefetchrows(self):
821+
"1267 - test prefetch rows using existing cursor"
822+
self.setup_round_trip_checker()
823+
824+
# Set prefetch rows on an existing cursor
825+
num_rows = 590
826+
with self.connection.cursor() as cursor:
827+
cursor.execute("truncate table TestTempTable")
828+
sql = "insert into TestTempTable (IntCol) values (:1)"
829+
data = [(n + 1,) for n in range(num_rows)]
830+
cursor.executemany(sql, data)
831+
cursor.prefetchrows = 300
832+
cursor.arraysize = 300
833+
cursor.execute("select IntCol from TestTempTable").fetchall()
834+
self.assertRoundTrips(4)
835+
836+
def test_1268_bind_names_with_single_line_comments(self):
837+
"1268 - test bindnames() with single line comments"
838+
self.cursor.prepare("""--begin :value2 := :a + :b + :c +:a +3; end;
839+
begin :value2 := :a + :c +3; end;
840+
""")
841+
self.assertEqual(self.cursor.bindnames(), ["VALUE2", "A", "C"])
842+
843+
def test_1269_bind_names_with_multi_line_comments(self):
844+
"1269 - test bindnames() with multi line comments"
845+
self.cursor.prepare("""/*--select * from :a where :a = 1
846+
select * from table_names where :a = 1*/
847+
select * from :table_name where :value = 1
848+
""")
849+
self.assertEqual(self.cursor.bindnames(), ["TABLE_NAME", "VALUE"])
850+
794851
if __name__ == "__main__":
795852
base.run_test_cases()

0 commit comments

Comments
 (0)