Added support for scrollable cursors.
This commit is contained in:
parent
150303d995
commit
3eeff96403
20
Connection.c
20
Connection.c
@ -53,7 +53,7 @@ static PyObject *Connection_Commit(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_Begin(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_Prepare(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_Rollback(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_NewCursor(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_NewCursor(udt_Connection*, PyObject*, PyObject*);
|
||||
static PyObject *Connection_Cancel(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_RegisterCallback(udt_Connection*, PyObject*);
|
||||
static PyObject *Connection_UnregisterCallback(udt_Connection*, PyObject*);
|
||||
@ -87,7 +87,8 @@ static PyObject *Connection_GetLTXID(udt_Connection*, void*);
|
||||
// declaration of methods for Python type "Connection"
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyMethodDef g_ConnectionMethods[] = {
|
||||
{ "cursor", (PyCFunction) Connection_NewCursor, METH_NOARGS },
|
||||
{ "cursor", (PyCFunction) Connection_NewCursor,
|
||||
METH_VARARGS | METH_KEYWORDS },
|
||||
{ "commit", (PyCFunction) Connection_Commit, METH_NOARGS },
|
||||
{ "rollback", (PyCFunction) Connection_Rollback, METH_NOARGS },
|
||||
{ "begin", (PyCFunction) Connection_Begin, METH_VARARGS },
|
||||
@ -1102,7 +1103,7 @@ static PyObject *Connection_GetVersion(
|
||||
}
|
||||
|
||||
// allocate a cursor to retrieve the version
|
||||
cursor = (udt_Cursor*) Connection_NewCursor(self, NULL);
|
||||
cursor = (udt_Cursor*) Connection_NewCursor(self, NULL, NULL);
|
||||
if (!cursor)
|
||||
return NULL;
|
||||
|
||||
@ -1489,16 +1490,23 @@ static PyObject *Connection_Rollback(
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *Connection_NewCursor(
|
||||
udt_Connection *self, // connection to create cursor on
|
||||
PyObject *args) // arguments
|
||||
PyObject *args, // arguments
|
||||
PyObject *keywordArgs) // keyword arguments
|
||||
{
|
||||
PyObject *createArgs, *result;
|
||||
Py_ssize_t numArgs = 0, i;
|
||||
|
||||
createArgs = PyTuple_New(1);
|
||||
if (args)
|
||||
numArgs = PyTuple_GET_SIZE(args);
|
||||
createArgs = PyTuple_New(1 + numArgs);
|
||||
if (!createArgs)
|
||||
return NULL;
|
||||
Py_INCREF(self);
|
||||
PyTuple_SET_ITEM(createArgs, 0, (PyObject*) self);
|
||||
result = PyObject_Call( (PyObject*) &g_CursorType, createArgs, NULL);
|
||||
for (i = 0; i < numArgs; i++)
|
||||
PyTuple_SET_ITEM(createArgs, i + 1, PyTuple_GET_ITEM(args, i));
|
||||
result = PyObject_Call( (PyObject*) &g_CursorType, createArgs,
|
||||
keywordArgs);
|
||||
Py_DECREF(createArgs);
|
||||
return result;
|
||||
}
|
||||
|
||||
146
Cursor.c
146
Cursor.c
@ -32,6 +32,7 @@ typedef struct {
|
||||
int hasRowsToFetch;
|
||||
int isOpen;
|
||||
int isOwned;
|
||||
boolean isScrollable;
|
||||
} udt_Cursor;
|
||||
|
||||
|
||||
@ -58,6 +59,7 @@ static PyObject *Cursor_FetchAll(udt_Cursor*, PyObject*);
|
||||
static PyObject *Cursor_FetchRaw(udt_Cursor*, PyObject*, PyObject*);
|
||||
static PyObject *Cursor_Parse(udt_Cursor*, PyObject*);
|
||||
static PyObject *Cursor_Prepare(udt_Cursor*, PyObject*);
|
||||
static PyObject *Cursor_Scroll(udt_Cursor*, PyObject*, PyObject*);
|
||||
static PyObject *Cursor_SetInputSizes(udt_Cursor*, PyObject*, PyObject*);
|
||||
static PyObject *Cursor_SetOutputSize(udt_Cursor*, PyObject*);
|
||||
static PyObject *Cursor_Var(udt_Cursor*, PyObject*, PyObject*);
|
||||
@ -98,6 +100,7 @@ static PyMethodDef g_CursorMethods[] = {
|
||||
{ "executemanyprepared", (PyCFunction) Cursor_ExecuteManyPrepared,
|
||||
METH_VARARGS },
|
||||
{ "setoutputsize", (PyCFunction) Cursor_SetOutputSize, METH_VARARGS },
|
||||
{ "scroll", (PyCFunction) Cursor_Scroll, METH_VARARGS | METH_KEYWORDS },
|
||||
{ "var", (PyCFunction) Cursor_Var, METH_VARARGS | METH_KEYWORDS },
|
||||
{ "arrayvar", (PyCFunction) Cursor_ArrayVar, METH_VARARGS },
|
||||
{ "bindnames", (PyCFunction) Cursor_BindNames, METH_NOARGS },
|
||||
@ -130,6 +133,7 @@ static PyMemberDef g_CursorMembers[] = {
|
||||
0 },
|
||||
{ "outputtypehandler", T_OBJECT, offsetof(udt_Cursor, outputTypeHandler),
|
||||
0 },
|
||||
{ "scrollable", T_BOOL, offsetof(udt_Cursor, isScrollable), 0 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -287,10 +291,14 @@ static int Cursor_Init(
|
||||
PyObject *args, // arguments
|
||||
PyObject *keywordArgs) // keyword arguments
|
||||
{
|
||||
static char *keywordList[] = { "connection", "scrollable", NULL };
|
||||
udt_Connection *connection;
|
||||
PyObject *scrollableObj;
|
||||
|
||||
// parse arguments
|
||||
if (!PyArg_ParseTuple(args, "O!", &g_ConnectionType, &connection))
|
||||
scrollableObj = NULL;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O", keywordList,
|
||||
&g_ConnectionType, &connection, &scrollableObj))
|
||||
return -1;
|
||||
|
||||
// initialize members
|
||||
@ -304,6 +312,11 @@ static int Cursor_Init(
|
||||
self->outputSize = -1;
|
||||
self->outputSizeColumn = -1;
|
||||
self->isOpen = 1;
|
||||
if (scrollableObj) {
|
||||
self->isScrollable = PyObject_IsTrue(scrollableObj);
|
||||
if (self->isScrollable < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1637,6 +1650,7 @@ static PyObject *Cursor_Execute(
|
||||
{
|
||||
PyObject *statement, *executeArgs;
|
||||
int isQuery;
|
||||
ub4 mode;
|
||||
|
||||
executeArgs = NULL;
|
||||
if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
|
||||
@ -1676,8 +1690,11 @@ static PyObject *Cursor_Execute(
|
||||
return NULL;
|
||||
|
||||
// execute the statement
|
||||
mode = OCI_DEFAULT;
|
||||
isQuery = (self->statementType == OCI_STMT_SELECT);
|
||||
if (Cursor_InternalExecute(self, isQuery ? 0 : 1, 0) < 0)
|
||||
if (isQuery && self->isScrollable)
|
||||
mode = OCI_STMT_SCROLLABLE_READONLY;
|
||||
if (Cursor_InternalExecute(self, isQuery ? 0 : 1, mode) < 0)
|
||||
return NULL;
|
||||
|
||||
// perform defines, if necessary
|
||||
@ -1851,9 +1868,9 @@ static int Cursor_InternalFetch(
|
||||
udt_Cursor *self, // cursor to fetch from
|
||||
int numRows) // number of rows to fetch
|
||||
{
|
||||
ub4 currentPosition;
|
||||
udt_Variable *var;
|
||||
sword status;
|
||||
ub8 rowCount;
|
||||
int i;
|
||||
|
||||
if (!self->fetchVariables) {
|
||||
@ -1878,14 +1895,22 @@ static int Cursor_InternalFetch(
|
||||
"Cursor_InternalFetch(): fetch") < 0)
|
||||
return -1;
|
||||
|
||||
// determine the number of rows fetched into buffers
|
||||
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &self->bufferRowCount, 0,
|
||||
OCI_ATTR_ROWS_FETCHED, self->environment->errorHandle);
|
||||
if (Environment_CheckForError(self->environment, status,
|
||||
"Cursor_InternalFetch(): get rows fetched") < 0)
|
||||
return -1;
|
||||
if (Cursor_GetRowCount(self, &rowCount) < 0)
|
||||
|
||||
// determine the current position in the cursor
|
||||
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, ¤tPosition, 0,
|
||||
OCI_ATTR_CURRENT_POSITION, self->environment->errorHandle);
|
||||
if (Environment_CheckForError(self->environment, status,
|
||||
"Cursor_InternalFetch(): get current position") < 0)
|
||||
return -1;
|
||||
self->rowCount = rowCount - self->bufferRowCount;
|
||||
|
||||
// reset buffer row index and row count
|
||||
self->rowCount = currentPosition - self->bufferRowCount;
|
||||
self->bufferRowIndex = 0;
|
||||
|
||||
return 0;
|
||||
@ -2061,6 +2086,115 @@ static PyObject *Cursor_FetchRaw(
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cursor_Scroll()
|
||||
// Scroll the cursor using the value and mode specified.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *Cursor_Scroll(
|
||||
udt_Cursor *self, // cursor to execute
|
||||
PyObject *args, // arguments
|
||||
PyObject *keywordArgs) // keyword arguments
|
||||
{
|
||||
static char *keywordList[] = { "value", "mode", NULL };
|
||||
ub8 desiredRow, minRowInBuffers, maxRowInBuffers;
|
||||
ub4 fetchMode, numRows, currentPosition;
|
||||
sword status;
|
||||
char *mode;
|
||||
int value;
|
||||
|
||||
// parse arguments
|
||||
value = 0;
|
||||
mode = NULL;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|is", keywordList,
|
||||
&value, &mode))
|
||||
return NULL;
|
||||
|
||||
// validate mode
|
||||
if (!mode) {
|
||||
fetchMode = OCI_FETCH_RELATIVE;
|
||||
desiredRow = self->rowCount + value;
|
||||
} else if (strcmp(mode, "relative") == 0) {
|
||||
fetchMode = OCI_FETCH_RELATIVE;
|
||||
desiredRow = self->rowCount + value;
|
||||
} else if (strcmp(mode, "absolute") == 0) {
|
||||
fetchMode = OCI_FETCH_ABSOLUTE;
|
||||
desiredRow = value;
|
||||
} else if (strcmp(mode, "first") == 0) {
|
||||
fetchMode = OCI_FETCH_FIRST;
|
||||
desiredRow = 1;
|
||||
} else if (strcmp(mode, "last") == 0) {
|
||||
fetchMode = OCI_FETCH_LAST;
|
||||
desiredRow = 0;
|
||||
} else {
|
||||
PyErr_SetString(g_InterfaceErrorException,
|
||||
"mode must be one of relative, absolute, first or last");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// make sure the cursor is open
|
||||
if (Cursor_IsOpen(self) < 0)
|
||||
return NULL;
|
||||
|
||||
// determine if a fetch is actually required; "last" is always fetched
|
||||
if (fetchMode != OCI_FETCH_LAST && self->bufferRowCount > 0) {
|
||||
minRowInBuffers = self->rowCount - self->bufferRowIndex;
|
||||
maxRowInBuffers = self->rowCount + self->bufferRowCount -
|
||||
self->bufferRowIndex - 1;
|
||||
if (self->bufferRowIndex == self->bufferRowCount) {
|
||||
minRowInBuffers += 1;
|
||||
maxRowInBuffers += 1;
|
||||
}
|
||||
if (desiredRow >= minRowInBuffers && desiredRow <= maxRowInBuffers) {
|
||||
self->bufferRowIndex = desiredRow - minRowInBuffers;
|
||||
self->rowCount = desiredRow - 1;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// perform fetch; when fetching the last row, only fetch a single row
|
||||
numRows = (fetchMode == OCI_FETCH_LAST) ? 1 : self->fetchArraySize;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
status = OCIStmtFetch2(self->handle, self->environment->errorHandle,
|
||||
numRows, fetchMode, value, OCI_DEFAULT);
|
||||
Py_END_ALLOW_THREADS
|
||||
if (status == OCI_NO_DATA) {
|
||||
if (fetchMode == OCI_FETCH_FIRST || fetchMode == OCI_FETCH_LAST) {
|
||||
self->hasRowsToFetch = 0;
|
||||
self->rowCount = 0;
|
||||
self->bufferRowCount = 0;
|
||||
self->bufferRowIndex = 0;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_IndexError,
|
||||
"requested scroll operation would leave result set");
|
||||
return NULL;
|
||||
}
|
||||
} else if (Environment_CheckForError(self->environment, status,
|
||||
"Cursor_Scroll(): fetch") < 0)
|
||||
return NULL;
|
||||
self->hasRowsToFetch = 1;
|
||||
|
||||
// determine the number of rows actually fetched
|
||||
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &self->bufferRowCount, 0,
|
||||
OCI_ATTR_ROWS_FETCHED, self->environment->errorHandle);
|
||||
if (Environment_CheckForError(self->environment, status,
|
||||
"Cursor_Scroll(): get rows fetched") < 0)
|
||||
return NULL;
|
||||
|
||||
// determine the current position of the cursor
|
||||
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, ¤tPosition, 0,
|
||||
OCI_ATTR_CURRENT_POSITION, self->environment->errorHandle);
|
||||
if (Environment_CheckForError(self->environment, status,
|
||||
"Cursor_Scroll(): get current position") < 0)
|
||||
return NULL;
|
||||
|
||||
// reset buffer row index and row count
|
||||
self->rowCount = currentPosition - self->bufferRowCount;
|
||||
self->bufferRowIndex = 0;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cursor_SetInputSizes()
|
||||
// Set the sizes of the bind variables.
|
||||
@ -2515,7 +2649,7 @@ static PyObject * Cursor_GetImplicitResults(
|
||||
// populate it with the implicit results
|
||||
for (i = 0; i < numImplicitResults; i++) {
|
||||
childCursor = (udt_Cursor*) Connection_NewCursor(self->connection,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (!childCursor) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
|
||||
@ -92,7 +92,8 @@ static int CursorVar_Initialize(
|
||||
if (!var->cursors)
|
||||
return -1;
|
||||
for (i = 0; i < var->allocatedElements; i++) {
|
||||
tempCursor = (udt_Cursor*) Connection_NewCursor(var->connection, NULL);
|
||||
tempCursor = (udt_Cursor*) Connection_NewCursor(var->connection, NULL,
|
||||
NULL);
|
||||
if (!tempCursor) {
|
||||
Py_DECREF(var);
|
||||
return -1;
|
||||
|
||||
@ -1007,7 +1007,7 @@ static PyObject *Subscription_RegisterQuery(
|
||||
|
||||
// create cursor to perform query
|
||||
env = self->connection->environment;
|
||||
cursor = (udt_Cursor*) Connection_NewCursor(self->connection, NULL);
|
||||
cursor = (udt_Cursor*) Connection_NewCursor(self->connection, NULL, NULL);
|
||||
if (!cursor)
|
||||
return NULL;
|
||||
|
||||
|
||||
@ -408,6 +408,42 @@ Cursor Object
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.scroll(value = 0, mode="relative")
|
||||
|
||||
Scroll the cursor in the result set to a new position according to the mode.
|
||||
|
||||
If mode is "relative" (the default value), the value is taken as an offset
|
||||
to the current position in the result set. If set to "absolute", value
|
||||
states an absolute target position. If set to "first", the cursor is
|
||||
positioned at the first row and if set to "last", the cursor is set to the
|
||||
last row in the result set.
|
||||
|
||||
An IndexError is raised if the mode is "relative" or "absolute" and the
|
||||
scroll operation would position the cursor outside of the result set.
|
||||
|
||||
.. versionadded:: development
|
||||
|
||||
.. note::
|
||||
|
||||
This method is an extension to the DB API definition but it is
|
||||
mentioned in PEP 249 as an optional extension.
|
||||
|
||||
|
||||
.. attribute:: Cursor.scrollable
|
||||
|
||||
This read-write boolean attribute specifies whether the cursor can be
|
||||
scrolled or not. By default, cursors are not scrollable, as the server
|
||||
resources and response times are greater than nonscrollable cursors. This
|
||||
attribute is checked and the corresponding mode set in Oracle when calling
|
||||
the method :meth:`~Cursor.execute()`.
|
||||
|
||||
.. versionadded:: development
|
||||
|
||||
.. note::
|
||||
|
||||
The DB API definition does not define this attribute.
|
||||
|
||||
|
||||
.. method:: Cursor.setinputsizes(\*args, \*\*keywordArgs)
|
||||
|
||||
This can be used before a call to execute(), callfunc() or callproc() to
|
||||
|
||||
145
test/Cursor.py
145
test/Cursor.py
@ -5,6 +5,17 @@ import sys
|
||||
|
||||
class TestCursor(BaseTestCase):
|
||||
|
||||
def testCreateScrollableCursor(self):
|
||||
"""test creating a scrollable cursor"""
|
||||
cursor = self.connection.cursor()
|
||||
self.assertEqual(cursor.scrollable, False)
|
||||
cursor = self.connection.cursor(True)
|
||||
self.assertEqual(cursor.scrollable, True)
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
self.assertEqual(cursor.scrollable, True)
|
||||
cursor.scrollable = False
|
||||
self.assertEqual(cursor.scrollable, False)
|
||||
|
||||
def testExecuteNoArgs(self):
|
||||
"""test executing a statement without any arguments"""
|
||||
result = self.cursor.execute("begin null; end;")
|
||||
@ -236,6 +247,140 @@ class TestCursor(BaseTestCase):
|
||||
self.assertRaises(cx_Oracle.InterfaceError,
|
||||
self.cursor.fetchall)
|
||||
|
||||
def testScrollAbsoluteExceptionAfter(self):
|
||||
"""test scrolling absolute yields an exception (after result set)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
self.assertRaises(IndexError, cursor.scroll, 12, "absolute")
|
||||
|
||||
def testScrollAbsoluteInBuffer(self):
|
||||
"""test scrolling absolute (when in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.fetchmany()
|
||||
self.assertTrue(cursor.arraysize > 1,
|
||||
"array size must exceed 1 for this test to work correctly")
|
||||
cursor.scroll(1, mode = "absolute")
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 1.25)
|
||||
self.assertEqual(cursor.rowcount, 1)
|
||||
|
||||
def testScrollAbsoluteNotInBuffer(self):
|
||||
"""test scrolling absolute (when not in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.scroll(6, mode = "absolute")
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 7.5)
|
||||
self.assertEqual(cursor.rowcount, 6)
|
||||
|
||||
def testScrollFirstInBuffer(self):
|
||||
"""test scrolling to first row in result set (when in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.fetchmany()
|
||||
cursor.scroll(mode = "first")
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 1.25)
|
||||
self.assertEqual(cursor.rowcount, 1)
|
||||
|
||||
def testScrollFirstNotInBuffer(self):
|
||||
"""test scrolling to first row in result set (when not in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.fetchmany()
|
||||
cursor.fetchmany()
|
||||
cursor.scroll(mode = "first")
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 1.25)
|
||||
self.assertEqual(cursor.rowcount, 1)
|
||||
|
||||
def testScrollLast(self):
|
||||
"""test scrolling to last row in result set"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.scroll(mode = "last")
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 12.5)
|
||||
self.assertEqual(cursor.rowcount, 10)
|
||||
|
||||
def testScrollRelativeExceptionAfter(self):
|
||||
"""test scrolling relative yields an exception (after result set)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
self.assertRaises(IndexError, cursor.scroll, 15)
|
||||
|
||||
def testScrollRelativeExceptionBefore(self):
|
||||
"""test scrolling relative yields an exception (before result set)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
self.assertRaises(IndexError, cursor.scroll, -5)
|
||||
|
||||
def testScrollRelativeInBuffer(self):
|
||||
"""test scrolling relative (when in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.fetchmany()
|
||||
self.assertTrue(cursor.arraysize > 1,
|
||||
"array size must exceed 1 for this test to work correctly")
|
||||
cursor.scroll(2 - cursor.rowcount)
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 2.5)
|
||||
self.assertEqual(cursor.rowcount, 2)
|
||||
|
||||
def testScrollRelativeNotInBuffer(self):
|
||||
"""test scrolling relative (when not in buffers)"""
|
||||
cursor = self.connection.cursor(scrollable = True)
|
||||
cursor.arraysize = self.cursor.arraysize
|
||||
cursor.execute("""
|
||||
select NumberCol
|
||||
from TestNumbers
|
||||
order by IntCol""")
|
||||
cursor.fetchmany()
|
||||
cursor.fetchmany()
|
||||
self.assertTrue(cursor.arraysize > 1,
|
||||
"array size must exceed 2 for this test to work correctly")
|
||||
cursor.scroll(3 - cursor.rowcount)
|
||||
row = cursor.fetchone()
|
||||
self.assertEqual(row[0], 3.75)
|
||||
self.assertEqual(cursor.rowcount, 3)
|
||||
|
||||
def testSetInputSizesMultipleMethod(self):
|
||||
"""test setting input sizes with both positional and keyword args"""
|
||||
self.assertRaises(cx_Oracle.InterfaceError,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user