Eliminate segfault when attempting to reuse a REF cursor that has been closed.

This commit is contained in:
Anthony Tuininga 2017-08-30 12:12:12 -06:00
parent d5ac142c24
commit 7d3b7b101f
2 changed files with 29 additions and 0 deletions

View File

@ -46,16 +46,27 @@ static int CursorVar_SetValue(udt_Variable *var, uint32_t pos, dpiData *data,
PyObject *value) PyObject *value)
{ {
udt_Cursor *cursor; udt_Cursor *cursor;
dpiStmtInfo info;
if (!PyObject_IsInstance(value, (PyObject*) &g_CursorType)) { if (!PyObject_IsInstance(value, (PyObject*) &g_CursorType)) {
PyErr_SetString(PyExc_TypeError, "expecting cursor"); PyErr_SetString(PyExc_TypeError, "expecting cursor");
return -1; return -1;
} }
// if the cursor already has a handle, use it directly
cursor = (udt_Cursor *) value; cursor = (udt_Cursor *) value;
if (cursor->handle) { if (cursor->handle) {
if (dpiVar_setFromStmt(var->handle, pos, cursor->handle) < 0) if (dpiVar_setFromStmt(var->handle, pos, cursor->handle) < 0)
return Error_RaiseAndReturnInt(); return Error_RaiseAndReturnInt();
// otherwise, make use of the statement handle allocated by the variable
// BUT, make sure the statement handle is still valid as it may have been
// closed by some other code; the call to dpiStmt_getInfo() will ensure the
// statement is still open; if an error occurs, this bind will be discarded
// and a second attempt will be made with a new cursor
} else { } else {
if (dpiStmt_getInfo(data->value.asStmt, &info) < 0)
return Error_RaiseAndReturnInt();
cursor->handle = data->value.asStmt; cursor->handle = data->value.asStmt;
dpiStmt_addRef(cursor->handle); dpiStmt_addRef(cursor->handle);
} }

View File

@ -50,6 +50,24 @@ class TestCursorVar(BaseTestCase):
self.assertRaises(cx_Oracle.DatabaseError, cursor.execute, sql, self.assertRaises(cx_Oracle.DatabaseError, cursor.execute, sql,
pcursor = cursor) pcursor = cursor)
def testExecuteAfterClose(self):
"test executing a statement returning a ref cursor after closing it"
outCursor = self.connection.cursor()
sql = """
begin
open :pcursor for
select IntCol
from TestNumbers
order by IntCol;
end;"""
self.cursor.execute(sql, pcursor = outCursor)
rows = outCursor.fetchall()
outCursor.close()
outCursor = self.connection.cursor()
self.cursor.execute(sql, pcursor = outCursor)
rows2 = outCursor.fetchall()
self.assertEqual(rows, rows2)
def testFetchCursor(self): def testFetchCursor(self):
"test fetching a cursor" "test fetching a cursor"
self.cursor.execute(""" self.cursor.execute("""