Skip to content

Commit 7d3b7b1

Browse files
Eliminate segfault when attempting to reuse a REF cursor that has been closed.
1 parent d5ac142 commit 7d3b7b1

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

src/CursorVar.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,27 @@ static int CursorVar_SetValue(udt_Variable *var, uint32_t pos, dpiData *data,
4646
PyObject *value)
4747
{
4848
udt_Cursor *cursor;
49+
dpiStmtInfo info;
4950

5051
if (!PyObject_IsInstance(value, (PyObject*) &g_CursorType)) {
5152
PyErr_SetString(PyExc_TypeError, "expecting cursor");
5253
return -1;
5354
}
55+
56+
// if the cursor already has a handle, use it directly
5457
cursor = (udt_Cursor *) value;
5558
if (cursor->handle) {
5659
if (dpiVar_setFromStmt(var->handle, pos, cursor->handle) < 0)
5760
return Error_RaiseAndReturnInt();
61+
62+
// otherwise, make use of the statement handle allocated by the variable
63+
// BUT, make sure the statement handle is still valid as it may have been
64+
// closed by some other code; the call to dpiStmt_getInfo() will ensure the
65+
// statement is still open; if an error occurs, this bind will be discarded
66+
// and a second attempt will be made with a new cursor
5867
} else {
68+
if (dpiStmt_getInfo(data->value.asStmt, &info) < 0)
69+
return Error_RaiseAndReturnInt();
5970
cursor->handle = data->value.asStmt;
6071
dpiStmt_addRef(cursor->handle);
6172
}

test/CursorVar.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ def testBindSelf(self):
5050
self.assertRaises(cx_Oracle.DatabaseError, cursor.execute, sql,
5151
pcursor = cursor)
5252

53+
def testExecuteAfterClose(self):
54+
"test executing a statement returning a ref cursor after closing it"
55+
outCursor = self.connection.cursor()
56+
sql = """
57+
begin
58+
open :pcursor for
59+
select IntCol
60+
from TestNumbers
61+
order by IntCol;
62+
end;"""
63+
self.cursor.execute(sql, pcursor = outCursor)
64+
rows = outCursor.fetchall()
65+
outCursor.close()
66+
outCursor = self.connection.cursor()
67+
self.cursor.execute(sql, pcursor = outCursor)
68+
rows2 = outCursor.fetchall()
69+
self.assertEqual(rows, rows2)
70+
5371
def testFetchCursor(self):
5472
"test fetching a cursor"
5573
self.cursor.execute("""

0 commit comments

Comments
 (0)