2271 lines
76 KiB
C
2271 lines
76 KiB
C
//-----------------------------------------------------------------------------
|
|
// Cursor.c
|
|
// Definition of the Python type OracleCursor.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// structure for the Python type "Cursor"
|
|
//-----------------------------------------------------------------------------
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
OCIStmt *handle;
|
|
udt_Connection *connection;
|
|
udt_Environment *environment;
|
|
PyObject *statement;
|
|
PyObject *statementTag;
|
|
PyObject *bindVariables;
|
|
PyObject *fetchVariables;
|
|
PyObject *rowFactory;
|
|
PyObject *inputTypeHandler;
|
|
PyObject *outputTypeHandler;
|
|
int arraySize;
|
|
int bindArraySize;
|
|
int fetchArraySize;
|
|
int numbersAsStrings;
|
|
int setInputSizes;
|
|
int outputSize;
|
|
int outputSizeColumn;
|
|
int rowCount;
|
|
int actualRows;
|
|
int rowNum;
|
|
int statementType;
|
|
int isDML;
|
|
int isOpen;
|
|
int isOwned;
|
|
} udt_Cursor;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// dependent function defintions
|
|
//-----------------------------------------------------------------------------
|
|
static void Cursor_Free(udt_Cursor*);
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// functions for the Python type "Cursor"
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_GetIter(udt_Cursor*);
|
|
static PyObject *Cursor_GetNext(udt_Cursor*);
|
|
static PyObject *Cursor_Close(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_CallFunc(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_CallProc(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_Execute(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_ExecuteMany(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_ExecuteManyPrepared(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_FetchOne(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_FetchMany(udt_Cursor*, PyObject*, PyObject*);
|
|
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_SetInputSizes(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_SetOutputSize(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_Var(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_ArrayVar(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_BindNames(udt_Cursor*, PyObject*);
|
|
static PyObject *Cursor_GetDescription(udt_Cursor*, void*);
|
|
static PyObject *Cursor_New(PyTypeObject*, PyObject*, PyObject*);
|
|
static int Cursor_Init(udt_Cursor*, PyObject*, PyObject*);
|
|
static PyObject *Cursor_Repr(udt_Cursor*);
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// declaration of methods for Python type "Cursor"
|
|
//-----------------------------------------------------------------------------
|
|
static PyMethodDef g_CursorMethods[] = {
|
|
{ "execute", (PyCFunction) Cursor_Execute, METH_VARARGS | METH_KEYWORDS },
|
|
{ "fetchall", (PyCFunction) Cursor_FetchAll, METH_NOARGS },
|
|
{ "fetchone", (PyCFunction) Cursor_FetchOne, METH_NOARGS },
|
|
{ "fetchmany", (PyCFunction) Cursor_FetchMany,
|
|
METH_VARARGS | METH_KEYWORDS },
|
|
{ "fetchraw", (PyCFunction) Cursor_FetchRaw,
|
|
METH_VARARGS | METH_KEYWORDS },
|
|
{ "prepare", (PyCFunction) Cursor_Prepare, METH_VARARGS },
|
|
{ "parse", (PyCFunction) Cursor_Parse, METH_VARARGS },
|
|
{ "setinputsizes", (PyCFunction) Cursor_SetInputSizes,
|
|
METH_VARARGS | METH_KEYWORDS },
|
|
{ "executemany", (PyCFunction) Cursor_ExecuteMany, METH_VARARGS },
|
|
{ "callproc", (PyCFunction) Cursor_CallProc,
|
|
METH_VARARGS | METH_KEYWORDS },
|
|
{ "callfunc", (PyCFunction) Cursor_CallFunc,
|
|
METH_VARARGS | METH_KEYWORDS },
|
|
{ "executemanyprepared", (PyCFunction) Cursor_ExecuteManyPrepared,
|
|
METH_VARARGS },
|
|
{ "setoutputsize", (PyCFunction) Cursor_SetOutputSize, METH_VARARGS },
|
|
{ "var", (PyCFunction) Cursor_Var, METH_VARARGS | METH_KEYWORDS },
|
|
{ "arrayvar", (PyCFunction) Cursor_ArrayVar, METH_VARARGS },
|
|
{ "bindnames", (PyCFunction) Cursor_BindNames, METH_NOARGS },
|
|
{ "close", (PyCFunction) Cursor_Close, METH_NOARGS },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// declaration of members for Python type "Cursor"
|
|
//-----------------------------------------------------------------------------
|
|
static PyMemberDef g_CursorMembers[] = {
|
|
{ "arraysize", T_INT, offsetof(udt_Cursor, arraySize), 0 },
|
|
{ "bindarraysize", T_INT, offsetof(udt_Cursor, bindArraySize), 0 },
|
|
{ "rowcount", T_INT, offsetof(udt_Cursor, rowCount), READONLY },
|
|
{ "statement", T_OBJECT, offsetof(udt_Cursor, statement), READONLY },
|
|
{ "connection", T_OBJECT_EX, offsetof(udt_Cursor, connection), READONLY },
|
|
{ "numbersAsStrings", T_INT, offsetof(udt_Cursor, numbersAsStrings), 0 },
|
|
{ "rowfactory", T_OBJECT, offsetof(udt_Cursor, rowFactory), 0 },
|
|
{ "bindvars", T_OBJECT, offsetof(udt_Cursor, bindVariables), READONLY },
|
|
{ "fetchvars", T_OBJECT, offsetof(udt_Cursor, fetchVariables), READONLY },
|
|
{ "inputtypehandler", T_OBJECT, offsetof(udt_Cursor, inputTypeHandler),
|
|
0 },
|
|
{ "outputtypehandler", T_OBJECT, offsetof(udt_Cursor, outputTypeHandler),
|
|
0 },
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// declaration of calculated members for Python type "Connection"
|
|
//-----------------------------------------------------------------------------
|
|
static PyGetSetDef g_CursorCalcMembers[] = {
|
|
{ "description", (getter) Cursor_GetDescription, 0, 0, 0 },
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// declaration of Python type "Cursor"
|
|
//-----------------------------------------------------------------------------
|
|
static PyTypeObject g_CursorType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"OracleCursor", // tp_name
|
|
sizeof(udt_Cursor), // tp_basicsize
|
|
0, // tp_itemsize
|
|
(destructor) Cursor_Free, // tp_dealloc
|
|
0, // tp_print
|
|
0, // tp_getattr
|
|
0, // tp_setattr
|
|
0, // tp_compare
|
|
(reprfunc) Cursor_Repr, // tp_repr
|
|
0, // tp_as_number
|
|
0, // tp_as_sequence
|
|
0, // tp_as_mapping
|
|
0, // tp_hash
|
|
0, // tp_call
|
|
0, // tp_str
|
|
0, // tp_getattro
|
|
0, // tp_setattro
|
|
0, // tp_as_buffer
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
// tp_flags
|
|
0, // tp_doc
|
|
0, // tp_traverse
|
|
0, // tp_clear
|
|
0, // tp_richcompare
|
|
0, // tp_weaklistoffset
|
|
(getiterfunc) Cursor_GetIter, // tp_iter
|
|
(iternextfunc) Cursor_GetNext, // tp_iternext
|
|
g_CursorMethods, // tp_methods
|
|
g_CursorMembers, // tp_members
|
|
g_CursorCalcMembers, // tp_getset
|
|
0, // tp_base
|
|
0, // tp_dict
|
|
0, // tp_descr_get
|
|
0, // tp_descr_set
|
|
0, // tp_dictoffset
|
|
(initproc) Cursor_Init, // tp_init
|
|
0, // tp_alloc
|
|
Cursor_New, // tp_new
|
|
0, // tp_free
|
|
0, // tp_is_gc
|
|
0 // tp_bases
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_AllocateHandle()
|
|
// Allocate a new handle.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_AllocateHandle(
|
|
udt_Cursor *self) // cursor object
|
|
{
|
|
sword status;
|
|
|
|
self->isOwned = 1;
|
|
status = OCIHandleAlloc(self->environment->handle,
|
|
(dvoid**) &self->handle, OCI_HTYPE_STMT, 0, 0);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_New()") < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FreeHandle()
|
|
// Free the handle which may be reallocated if necessary.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_FreeHandle(
|
|
udt_Cursor *self, // cursor object
|
|
int raiseException) // raise an exception, if necesary?
|
|
{
|
|
udt_Buffer buffer;
|
|
sword status;
|
|
|
|
if (self->handle) {
|
|
if (self->isOwned) {
|
|
status = OCIHandleFree(self->handle, OCI_HTYPE_STMT);
|
|
if (raiseException && Environment_CheckForError(
|
|
self->environment, status, "Cursor_FreeHandle()") < 0)
|
|
return -1;
|
|
} else if (self->connection->handle != 0) {
|
|
if (!cxBuffer_FromObject(&buffer, self->statementTag,
|
|
self->environment->encoding) < 0)
|
|
return (raiseException) ? -1 : 0;
|
|
status = OCIStmtRelease(self->handle,
|
|
self->environment->errorHandle, (text*) buffer.ptr,
|
|
buffer.size, OCI_DEFAULT);
|
|
cxBuffer_Clear(&buffer);
|
|
if (raiseException && Environment_CheckForError(
|
|
self->environment, status, "Cursor_FreeHandle()") < 0)
|
|
return -1;
|
|
}
|
|
self->handle = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#include "Variable.c"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_IsOpen()
|
|
// Determines if the cursor object is open and if so, if the connection is
|
|
// also open.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_IsOpen(
|
|
udt_Cursor *self) // cursor to check
|
|
{
|
|
if (!self->isOpen) {
|
|
PyErr_SetString(g_InterfaceErrorException, "not open");
|
|
return -1;
|
|
}
|
|
return Connection_IsConnected(self->connection);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_New()
|
|
// Create a new cursor object.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_New(
|
|
PyTypeObject *type, // type object
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
return type->tp_alloc(type, 0);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Init()
|
|
// Create a new cursor object.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_Init(
|
|
udt_Cursor *self, // cursor object
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
udt_Connection *connection;
|
|
|
|
// parse arguments
|
|
if (!PyArg_ParseTuple(args, "O!", &g_ConnectionType, &connection))
|
|
return -1;
|
|
|
|
// initialize members
|
|
Py_INCREF(connection);
|
|
self->connection = connection;
|
|
self->environment = connection->environment;
|
|
self->arraySize = 50;
|
|
self->fetchArraySize = 50;
|
|
self->bindArraySize = 1;
|
|
self->statementType = -1;
|
|
self->outputSize = -1;
|
|
self->outputSizeColumn = -1;
|
|
self->isOpen = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Repr()
|
|
// Return a string representation of the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Repr(
|
|
udt_Cursor *cursor) // cursor to return the string for
|
|
{
|
|
PyObject *connectionRepr, *module, *name, *result, *format, *formatArgs;
|
|
|
|
format = cxString_FromAscii("<%s.%s on %s>");
|
|
if (!format)
|
|
return NULL;
|
|
connectionRepr = PyObject_Repr((PyObject*) cursor->connection);
|
|
if (!connectionRepr) {
|
|
Py_DECREF(format);
|
|
return NULL;
|
|
}
|
|
if (GetModuleAndName(Py_TYPE(cursor), &module, &name) < 0) {
|
|
Py_DECREF(format);
|
|
Py_DECREF(connectionRepr);
|
|
return NULL;
|
|
}
|
|
formatArgs = PyTuple_Pack(3, module, name, connectionRepr);
|
|
Py_DECREF(module);
|
|
Py_DECREF(name);
|
|
Py_DECREF(connectionRepr);
|
|
if (!formatArgs) {
|
|
Py_DECREF(format);
|
|
return NULL;
|
|
}
|
|
result = cxString_Format(format, formatArgs);
|
|
Py_DECREF(format);
|
|
Py_DECREF(formatArgs);
|
|
return result;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Free()
|
|
// Deallocate the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static void Cursor_Free(
|
|
udt_Cursor *self) // cursor object
|
|
{
|
|
Cursor_FreeHandle(self, 0);
|
|
Py_CLEAR(self->statement);
|
|
Py_CLEAR(self->statementTag);
|
|
Py_CLEAR(self->bindVariables);
|
|
Py_CLEAR(self->fetchVariables);
|
|
Py_CLEAR(self->connection);
|
|
Py_CLEAR(self->rowFactory);
|
|
Py_CLEAR(self->inputTypeHandler);
|
|
Py_CLEAR(self->outputTypeHandler);
|
|
Py_TYPE(self)->tp_free((PyObject*) self);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_GetBindNames()
|
|
// Return a list of bind variable names. At this point the cursor must have
|
|
// already been prepared.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_GetBindNames(
|
|
udt_Cursor *self, // cursor to get information from
|
|
int numElements, // number of elements (IN/OUT)
|
|
PyObject **names) // list of names (OUT)
|
|
{
|
|
ub1 *bindNameLengths, *indicatorNameLengths, *duplicate;
|
|
char *buffer, **bindNames, **indicatorNames;
|
|
OCIBind **bindHandles;
|
|
int elementSize, i;
|
|
sb4 foundElements;
|
|
PyObject *temp;
|
|
sword status;
|
|
|
|
// ensure that a statement has already been prepared
|
|
if (!self->statement) {
|
|
PyErr_SetString(g_ProgrammingErrorException,
|
|
"statement must be prepared first");
|
|
return -1;
|
|
}
|
|
|
|
// avoid bus errors on 64-bit platforms
|
|
numElements = numElements + (sizeof(void*) - numElements % sizeof(void*));
|
|
|
|
// initialize the buffers
|
|
elementSize = sizeof(char*) + sizeof(ub1) + sizeof(char*) + sizeof(ub1) +
|
|
sizeof(ub1) + sizeof(OCIBind*);
|
|
buffer = (char*) PyMem_Malloc(numElements * elementSize);
|
|
if (!buffer) {
|
|
PyErr_NoMemory();
|
|
return -1;
|
|
}
|
|
bindNames = (char**) buffer;
|
|
bindNameLengths = (ub1*) (((char*) bindNames) +
|
|
sizeof(char*) * numElements);
|
|
indicatorNames = (char**) (((char*) bindNameLengths) +
|
|
sizeof(ub1) * numElements);
|
|
indicatorNameLengths = (ub1*) (((char*) indicatorNames) +
|
|
sizeof(char*) * numElements);
|
|
duplicate = (ub1*) (((char*) indicatorNameLengths) +
|
|
sizeof(ub1) * numElements);
|
|
bindHandles = (OCIBind**) (((char*) duplicate) +
|
|
sizeof(ub1) * numElements);
|
|
|
|
// get the bind information
|
|
status = OCIStmtGetBindInfo(self->handle,
|
|
self->environment->errorHandle, numElements, 1, &foundElements,
|
|
(text**) bindNames, bindNameLengths, (text**) indicatorNames,
|
|
indicatorNameLengths, duplicate, bindHandles);
|
|
if (status != OCI_NO_DATA &&
|
|
Environment_CheckForError(self->environment, status,
|
|
"Cursor_GetBindNames()") < 0) {
|
|
PyMem_Free(buffer);
|
|
return -1;
|
|
}
|
|
if (foundElements < 0) {
|
|
*names = NULL;
|
|
PyMem_Free(buffer);
|
|
return abs(foundElements);
|
|
}
|
|
|
|
// create the list which is to be returned
|
|
*names = PyList_New(0);
|
|
if (!*names) {
|
|
PyMem_Free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
// process the bind information returned
|
|
for (i = 0; i < foundElements; i++) {
|
|
if (!duplicate[i]) {
|
|
temp = cxString_FromEncodedString(bindNames[i],
|
|
bindNameLengths[i],
|
|
self->connection->environment->encoding);
|
|
if (!temp) {
|
|
Py_DECREF(*names);
|
|
PyMem_Free(buffer);
|
|
return -1;
|
|
}
|
|
if (PyList_Append(*names, temp) < 0) {
|
|
Py_DECREF(*names);
|
|
Py_DECREF(temp);
|
|
PyMem_Free(buffer);
|
|
return -1;
|
|
}
|
|
Py_DECREF(temp);
|
|
}
|
|
}
|
|
PyMem_Free(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_PerformDefine()
|
|
// Perform the defines for the cursor. At this point it is assumed that the
|
|
// statement being executed is in fact a query.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_PerformDefine(
|
|
udt_Cursor *self) // cursor to perform define on
|
|
{
|
|
int numParams, pos;
|
|
udt_Variable *var;
|
|
sword status;
|
|
|
|
// determine number of items in select-list
|
|
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, (dvoid*) &numParams, 0,
|
|
OCI_ATTR_PARAM_COUNT, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_PerformDefine()") < 0)
|
|
return -1;
|
|
|
|
// create a list corresponding to the number of items
|
|
self->fetchVariables = PyList_New(numParams);
|
|
if (!self->fetchVariables)
|
|
return -1;
|
|
|
|
// define a variable for each select-item
|
|
self->fetchArraySize = self->arraySize;
|
|
for (pos = 1; pos <= numParams; pos++) {
|
|
var = Variable_Define(self, self->fetchArraySize, pos);
|
|
if (!var)
|
|
return -1;
|
|
PyList_SET_ITEM(self->fetchVariables, pos - 1, (PyObject *) var);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetRowCount()
|
|
// Set the rowcount variable.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_SetRowCount(
|
|
udt_Cursor *self) // cursor to set the rowcount on
|
|
{
|
|
ub4 rowCount;
|
|
sword status;
|
|
|
|
if (self->statementType == OCI_STMT_SELECT) {
|
|
self->rowCount = 0;
|
|
self->actualRows = -1;
|
|
self->rowNum = 0;
|
|
} else if (self->statementType == OCI_STMT_INSERT ||
|
|
self->statementType == OCI_STMT_UPDATE ||
|
|
self->statementType == OCI_STMT_DELETE) {
|
|
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &rowCount, 0,
|
|
OCI_ATTR_ROW_COUNT, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_SetRowCount()") < 0)
|
|
return -1;
|
|
self->rowCount = rowCount;
|
|
} else {
|
|
self->rowCount = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetErrorOffset()
|
|
// Set the error offset on the error object, if applicable.
|
|
//-----------------------------------------------------------------------------
|
|
static void Cursor_SetErrorOffset(
|
|
udt_Cursor *self) // cursor to get the error offset from
|
|
{
|
|
PyObject *type, *value, *traceback;
|
|
udt_Error *error;
|
|
|
|
PyErr_Fetch(&type, &value, &traceback);
|
|
if (type == g_DatabaseErrorException) {
|
|
error = (udt_Error*) value;
|
|
OCIAttrGet(self->handle, OCI_HTYPE_STMT, &error->offset, 0,
|
|
OCI_ATTR_PARSE_ERROR_OFFSET, self->environment->errorHandle);
|
|
}
|
|
PyErr_Restore(type, value, traceback);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_InternalExecute()
|
|
// Perform the work of executing a cursor and set the rowcount appropriately
|
|
// regardless of whether an error takes place.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_InternalExecute(
|
|
udt_Cursor *self, // cursor to perform the execute on
|
|
ub4 numIters) // number of iterations to execute
|
|
{
|
|
sword status;
|
|
ub4 mode;
|
|
|
|
if (self->connection->autocommit)
|
|
mode = OCI_COMMIT_ON_SUCCESS;
|
|
else mode = OCI_DEFAULT;
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
status = OCIStmtExecute(self->connection->handle, self->handle,
|
|
self->environment->errorHandle, numIters, 0, 0, 0, mode);
|
|
Py_END_ALLOW_THREADS
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_InternalExecute()") < 0) {
|
|
Cursor_SetErrorOffset(self);
|
|
if (Cursor_SetRowCount(self) < 0)
|
|
PyErr_Clear();
|
|
return -1;
|
|
}
|
|
return Cursor_SetRowCount(self);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_GetStatementType()
|
|
// Determine if the cursor is executing a select statement.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_GetStatementType(
|
|
udt_Cursor *self) // cursor to perform binds on
|
|
{
|
|
ub2 statementType;
|
|
sword status;
|
|
|
|
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT,
|
|
(dvoid*) &statementType, 0, OCI_ATTR_STMT_TYPE,
|
|
self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_GetStatementType()") < 0)
|
|
return -1;
|
|
self->statementType = statementType;
|
|
if (self->fetchVariables) {
|
|
Py_DECREF(self->fetchVariables);
|
|
self->fetchVariables = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FixupBoundCursor()
|
|
// Fixup a cursor so that fetching and returning cursor descriptions are
|
|
// successful after binding a cursor to another cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_FixupBoundCursor(
|
|
udt_Cursor *self) // cursor that may have been bound
|
|
{
|
|
if (self->handle && self->statementType < 0) {
|
|
if (Cursor_GetStatementType(self) < 0)
|
|
return -1;
|
|
if (self->statementType == OCI_STMT_SELECT &&
|
|
Cursor_PerformDefine(self) < 0)
|
|
return -1;
|
|
if (Cursor_SetRowCount(self) < 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_ItemDescriptionHelper()
|
|
// Helper for Cursor_ItemDescription() used so that it is not necessary to
|
|
// constantly free the descriptor when an error takes place.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_ItemDescriptionHelper(
|
|
udt_Cursor *self, // cursor object
|
|
unsigned pos, // position in description
|
|
OCIParam *param) // parameter to use for description
|
|
{
|
|
ub2 internalSize, charSize;
|
|
udt_VariableType *varType;
|
|
int displaySize, index;
|
|
PyObject *tuple, *type;
|
|
ub4 nameLength;
|
|
sb2 precision;
|
|
sword status;
|
|
char *name;
|
|
ub1 nullOk;
|
|
sb1 scale;
|
|
|
|
// acquire usable type of item
|
|
varType = Variable_TypeByOracleDescriptor(param, self->environment);
|
|
if (!varType)
|
|
return NULL;
|
|
|
|
// acquire internal size of item
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &internalSize, 0,
|
|
OCI_ATTR_DATA_SIZE, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): internal size") < 0)
|
|
return NULL;
|
|
|
|
// acquire character size of item
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &charSize, 0,
|
|
OCI_ATTR_CHAR_SIZE, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): character size") < 0)
|
|
return NULL;
|
|
|
|
// aquire name of item
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &name,
|
|
&nameLength, OCI_ATTR_NAME, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): name") < 0)
|
|
return NULL;
|
|
|
|
// lookup precision and scale
|
|
scale = 0;
|
|
precision = 0;
|
|
if (varType->pythonType == &g_NumberVarType) {
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &scale, 0,
|
|
OCI_ATTR_SCALE, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): scale") < 0)
|
|
return NULL;
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &precision, 0,
|
|
OCI_ATTR_PRECISION, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): precision") < 0)
|
|
return NULL;
|
|
}
|
|
|
|
// lookup whether null is permitted for the attribute
|
|
status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &nullOk, 0,
|
|
OCI_ATTR_IS_NULL, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): nullable") < 0)
|
|
return NULL;
|
|
|
|
// set display size based on data type
|
|
type = (PyObject*) varType->pythonType;
|
|
if (type == (PyObject*) &g_StringVarType)
|
|
displaySize = charSize;
|
|
#if PY_MAJOR_VERSION < 3
|
|
else if (type == (PyObject*) &g_UnicodeVarType)
|
|
displaySize = charSize;
|
|
#endif
|
|
else if (type == (PyObject*) &g_BinaryVarType)
|
|
displaySize = internalSize;
|
|
else if (type == (PyObject*) &g_FixedCharVarType)
|
|
displaySize = charSize;
|
|
#if PY_MAJOR_VERSION < 3
|
|
else if (type == (PyObject*) &g_FixedUnicodeVarType)
|
|
displaySize = charSize;
|
|
#endif
|
|
else if (type == (PyObject*) &g_NumberVarType) {
|
|
if (precision) {
|
|
displaySize = precision + 1;
|
|
if (scale > 0)
|
|
displaySize += scale + 1;
|
|
}
|
|
else displaySize = 127;
|
|
} else if (type == (PyObject*) &g_DateTimeVarType) {
|
|
displaySize = 23;
|
|
} else {
|
|
displaySize = -1;
|
|
}
|
|
|
|
// create the tuple and populate it
|
|
tuple = PyTuple_New(7);
|
|
if (!tuple)
|
|
return NULL;
|
|
|
|
// set each of the items in the tuple
|
|
PyTuple_SET_ITEM(tuple, 0, cxString_FromEncodedString(name, nameLength,
|
|
self->connection->environment->encoding));
|
|
Py_INCREF(type);
|
|
PyTuple_SET_ITEM(tuple, 1, type);
|
|
PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(displaySize));
|
|
PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(internalSize));
|
|
PyTuple_SET_ITEM(tuple, 4, PyInt_FromLong(precision));
|
|
PyTuple_SET_ITEM(tuple, 5, PyInt_FromLong(scale));
|
|
PyTuple_SET_ITEM(tuple, 6, PyInt_FromLong(nullOk != 0));
|
|
|
|
// make sure the tuple is ok
|
|
for (index = 0; index < 7; index++) {
|
|
if (!PyTuple_GET_ITEM(tuple, index)) {
|
|
Py_DECREF(tuple);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return tuple;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_ItemDescription()
|
|
// Return a tuple describing the item at the given position.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_ItemDescription(
|
|
udt_Cursor *self, // cursor object
|
|
unsigned pos) // position
|
|
{
|
|
PyObject *tuple;
|
|
OCIParam *param;
|
|
sword status;
|
|
|
|
// acquire parameter descriptor
|
|
status = OCIParamGet(self->handle, OCI_HTYPE_STMT,
|
|
self->environment->errorHandle, (void**) ¶m, pos);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_ItemDescription(): parameter") < 0)
|
|
return NULL;
|
|
|
|
// use helper routine to get tuple
|
|
tuple = Cursor_ItemDescriptionHelper(self, pos, param);
|
|
OCIDescriptorFree(param, OCI_DTYPE_PARAM);
|
|
return tuple;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_GetDescription()
|
|
// Return a list of 7-tuples consisting of the description of the define
|
|
// variables.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_GetDescription(
|
|
udt_Cursor *self, // cursor object
|
|
void *arg) // optional argument (ignored)
|
|
{
|
|
PyObject *results, *tuple;
|
|
int numItems, index;
|
|
sword status;
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// fixup bound cursor, if necessary
|
|
if (Cursor_FixupBoundCursor(self) < 0)
|
|
return NULL;
|
|
|
|
// if not a query, return None
|
|
if (self->statementType != OCI_STMT_SELECT) {
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
// determine number of items in select-list
|
|
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, (dvoid*) &numItems, 0,
|
|
OCI_ATTR_PARAM_COUNT, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_GetDescription()") < 0)
|
|
return NULL;
|
|
|
|
// create a list of the required length
|
|
results = PyList_New(numItems);
|
|
if (!results)
|
|
return NULL;
|
|
|
|
// create tuples corresponding to the select-items
|
|
for (index = 0; index < numItems; index++) {
|
|
tuple = Cursor_ItemDescription(self, index + 1);
|
|
if (!tuple) {
|
|
Py_DECREF(results);
|
|
return NULL;
|
|
}
|
|
PyList_SET_ITEM(results, index, tuple);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Close()
|
|
// Close the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Close(
|
|
udt_Cursor *self, // cursor to close
|
|
PyObject *args) // arguments
|
|
{
|
|
// make sure we are actually open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// close the cursor
|
|
if (Cursor_FreeHandle(self, 1) < 0)
|
|
return NULL;
|
|
|
|
self->isOpen = 0;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetBindVariableHelper()
|
|
// Helper for setting a bind variable.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_SetBindVariableHelper(
|
|
udt_Cursor *self, // cursor to perform bind on
|
|
unsigned numElements, // number of elements to create
|
|
unsigned arrayPos, // array position to set
|
|
PyObject *value, // value to bind
|
|
udt_Variable *origVar, // original variable bound
|
|
udt_Variable **newVar, // new variable to be bound
|
|
int deferTypeAssignment) // defer type assignment if null?
|
|
{
|
|
int isValueVar;
|
|
|
|
// initialization
|
|
*newVar = NULL;
|
|
isValueVar = Variable_Check(value);
|
|
|
|
// handle case where variable is already bound
|
|
if (origVar) {
|
|
|
|
// if the value is a variable object, rebind it if necessary
|
|
if (isValueVar) {
|
|
if ( (PyObject*) origVar != value) {
|
|
Py_INCREF(value);
|
|
*newVar = (udt_Variable*) value;
|
|
}
|
|
|
|
// if the number of elements has changed, create a new variable
|
|
// this is only necessary for executemany() since execute() always
|
|
// passes a value of 1 for the number of elements
|
|
} else if (numElements > origVar->allocatedElements) {
|
|
*newVar = Variable_New(self, numElements, origVar->type,
|
|
origVar->size);
|
|
if (!*newVar)
|
|
return -1;
|
|
if (Variable_SetValue(*newVar, arrayPos, value) < 0)
|
|
return -1;
|
|
|
|
// otherwise, attempt to set the value
|
|
} else if (Variable_SetValue(origVar, arrayPos, value) < 0) {
|
|
|
|
// executemany() should simply fail after the first element
|
|
if (arrayPos > 0)
|
|
return -1;
|
|
|
|
// anything other than index error or type error should fail
|
|
if (!PyErr_ExceptionMatches(PyExc_IndexError) &&
|
|
!PyErr_ExceptionMatches(PyExc_TypeError))
|
|
return -1;
|
|
|
|
// clear the exception and try to create a new variable
|
|
PyErr_Clear();
|
|
origVar = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
// if no original variable used, create a new one
|
|
if (!origVar) {
|
|
|
|
// if the value is a variable object, bind it directly
|
|
if (isValueVar) {
|
|
Py_INCREF(value);
|
|
*newVar = (udt_Variable*) value;
|
|
(*newVar)->boundPos = 0;
|
|
Py_XDECREF((*newVar)->boundName);
|
|
(*newVar)->boundName = NULL;
|
|
|
|
// otherwise, create a new variable, unless the value is None and
|
|
// we wish to defer type assignment
|
|
} else if (value != Py_None || !deferTypeAssignment) {
|
|
*newVar = Variable_NewByValue(self, value, numElements);
|
|
if (!*newVar)
|
|
return -1;
|
|
if (Variable_SetValue(*newVar, arrayPos, value) < 0)
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetBindVariables()
|
|
// Create or set bind variables.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_SetBindVariables(
|
|
udt_Cursor *self, // cursor to perform binds on
|
|
PyObject *parameters, // parameters to bind
|
|
unsigned numElements, // number of elements to create
|
|
unsigned arrayPos, // array position to set
|
|
int deferTypeAssignment) // defer type assignment if null?
|
|
{
|
|
int i, origBoundByPos, origNumParams, boundByPos, numParams;
|
|
PyObject *key, *value, *origVar;
|
|
udt_Variable *newVar;
|
|
Py_ssize_t pos;
|
|
|
|
// make sure positional and named binds are not being intermixed
|
|
numParams = 0;
|
|
boundByPos = PySequence_Check(parameters);
|
|
if (boundByPos) {
|
|
numParams = PySequence_Size(parameters);
|
|
if (numParams < 0)
|
|
return -1;
|
|
}
|
|
if (self->bindVariables) {
|
|
origBoundByPos = PyList_Check(self->bindVariables);
|
|
if (boundByPos != origBoundByPos) {
|
|
PyErr_SetString(g_ProgrammingErrorException,
|
|
"positional and named binds cannot be intermixed");
|
|
return -1;
|
|
}
|
|
origNumParams = PyList_GET_SIZE(self->bindVariables);
|
|
|
|
// otherwise, create the list or dictionary if needed
|
|
} else {
|
|
if (boundByPos)
|
|
self->bindVariables = PyList_New(numParams);
|
|
else self->bindVariables = PyDict_New();
|
|
if (!self->bindVariables)
|
|
return -1;
|
|
origNumParams = 0;
|
|
}
|
|
|
|
// handle positional binds
|
|
if (boundByPos) {
|
|
for (i = 0; i < numParams; i++) {
|
|
value = PySequence_GetItem(parameters, i);
|
|
if (!value)
|
|
return -1;
|
|
Py_DECREF(value);
|
|
if (i < origNumParams) {
|
|
origVar = PyList_GET_ITEM(self->bindVariables, i);
|
|
if (origVar == Py_None)
|
|
origVar = NULL;
|
|
} else origVar = NULL;
|
|
if (Cursor_SetBindVariableHelper(self, numElements, arrayPos,
|
|
value, (udt_Variable*) origVar, &newVar,
|
|
deferTypeAssignment) < 0)
|
|
return -1;
|
|
if (newVar) {
|
|
if (i < PyList_GET_SIZE(self->bindVariables)) {
|
|
if (PyList_SetItem(self->bindVariables, i,
|
|
(PyObject*) newVar) < 0) {
|
|
Py_DECREF(newVar);
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (PyList_Append(self->bindVariables,
|
|
(PyObject*) newVar) < 0) {
|
|
Py_DECREF(newVar);
|
|
return -1;
|
|
}
|
|
Py_DECREF(newVar);
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle named binds
|
|
} else {
|
|
pos = 0;
|
|
while (PyDict_Next(parameters, &pos, &key, &value)) {
|
|
origVar = PyDict_GetItem(self->bindVariables, key);
|
|
if (Cursor_SetBindVariableHelper(self, numElements, arrayPos,
|
|
value, (udt_Variable*) origVar, &newVar,
|
|
deferTypeAssignment) < 0)
|
|
return -1;
|
|
if (newVar) {
|
|
if (PyDict_SetItem(self->bindVariables, key,
|
|
(PyObject*) newVar) < 0) {
|
|
Py_DECREF(newVar);
|
|
return -1;
|
|
}
|
|
Py_DECREF(newVar);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_PerformBind()
|
|
// Perform the binds on the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_PerformBind(
|
|
udt_Cursor *self) // cursor to perform binds on
|
|
{
|
|
PyObject *key, *var;
|
|
Py_ssize_t pos;
|
|
ub2 i;
|
|
|
|
// ensure that input sizes are reset
|
|
// this is done before binding is attempted so that if binding fails and
|
|
// a new statement is prepared, the bind variables will be reset and
|
|
// spurious errors will not occur
|
|
self->setInputSizes = 0;
|
|
|
|
// set values and perform binds for all bind variables
|
|
if (self->bindVariables) {
|
|
if (PyDict_Check(self->bindVariables)) {
|
|
pos = 0;
|
|
while (PyDict_Next(self->bindVariables, &pos, &key, &var)) {
|
|
if (Variable_Bind((udt_Variable*) var, self, key, 0) < 0)
|
|
return -1;
|
|
}
|
|
} else {
|
|
for (i = 0; i < (ub2) PyList_GET_SIZE(self->bindVariables); i++) {
|
|
var = PyList_GET_ITEM(self->bindVariables, i);
|
|
if (var != Py_None) {
|
|
if (Variable_Bind((udt_Variable*) var, self, NULL,
|
|
i + 1) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_CreateRow()
|
|
// Create an object for the row. The object created is a tuple unless a row
|
|
// factory function has been defined in which case it is the result of the
|
|
// row factory function called with the argument tuple that would otherwise be
|
|
// returned.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_CreateRow(
|
|
udt_Cursor *self) // cursor object
|
|
{
|
|
PyObject *tuple, *item, *result;
|
|
int numItems, pos;
|
|
udt_Variable *var;
|
|
|
|
// create a new tuple
|
|
numItems = PyList_GET_SIZE(self->fetchVariables);
|
|
tuple = PyTuple_New(numItems);
|
|
if (!tuple)
|
|
return NULL;
|
|
|
|
// acquire the value for each item
|
|
for (pos = 0; pos < numItems; pos++) {
|
|
var = (udt_Variable*) PyList_GET_ITEM(self->fetchVariables, pos);
|
|
item = Variable_GetValue(var, self->rowNum);
|
|
if (!item) {
|
|
Py_DECREF(tuple);
|
|
return NULL;
|
|
}
|
|
PyTuple_SET_ITEM(tuple, pos, item);
|
|
}
|
|
|
|
// increment row counters
|
|
self->rowNum++;
|
|
self->rowCount++;
|
|
|
|
// if a row factory is defined, call it
|
|
if (self->rowFactory && self->rowFactory != Py_None) {
|
|
result = PyObject_CallObject(self->rowFactory, tuple);
|
|
Py_DECREF(tuple);
|
|
return result;
|
|
}
|
|
|
|
return tuple;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_InternalPrepare()
|
|
// Internal method for preparing a statement for execution.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_InternalPrepare(
|
|
udt_Cursor *self, // cursor to perform prepare on
|
|
PyObject *statement, // statement to prepare
|
|
PyObject *statementTag) // tag of statement to prepare
|
|
{
|
|
udt_Buffer statementBuffer, tagBuffer;
|
|
sword status;
|
|
|
|
// make sure we don't get a situation where nothing is to be executed
|
|
if (statement == Py_None && !self->statement) {
|
|
PyErr_SetString(g_ProgrammingErrorException,
|
|
"no statement specified and no prior statement prepared");
|
|
return -1;
|
|
}
|
|
|
|
// nothing to do if the statement is identical to the one already stored
|
|
// but go ahead and prepare anyway for create, alter and drop statments
|
|
if (statement == Py_None || statement == self->statement) {
|
|
if (self->statementType != OCI_STMT_CREATE &&
|
|
self->statementType != OCI_STMT_DROP &&
|
|
self->statementType != OCI_STMT_ALTER)
|
|
return 0;
|
|
statement = self->statement;
|
|
}
|
|
|
|
// keep track of the statement
|
|
Py_XDECREF(self->statement);
|
|
Py_INCREF(statement);
|
|
self->statement = statement;
|
|
|
|
// release existing statement, if necessary
|
|
Py_XDECREF(self->statementTag);
|
|
Py_XINCREF(statementTag);
|
|
self->statementTag = statementTag;
|
|
if (Cursor_FreeHandle(self, 1) < 0)
|
|
return -1;
|
|
|
|
// prepare statement
|
|
self->isOwned = 0;
|
|
if (cxBuffer_FromObject(&statementBuffer, statement,
|
|
self->environment->encoding) < 0)
|
|
return -1;
|
|
if (cxBuffer_FromObject(&tagBuffer, statementTag,
|
|
self->environment->encoding) < 0) {
|
|
cxBuffer_Clear(&statementBuffer);
|
|
return -1;
|
|
}
|
|
Py_BEGIN_ALLOW_THREADS
|
|
status = OCIStmtPrepare2(self->connection->handle, &self->handle,
|
|
self->environment->errorHandle, (text*) statementBuffer.ptr,
|
|
statementBuffer.size, (text*) tagBuffer.ptr, tagBuffer.size,
|
|
OCI_NTV_SYNTAX, OCI_DEFAULT);
|
|
Py_END_ALLOW_THREADS
|
|
cxBuffer_Clear(&statementBuffer);
|
|
cxBuffer_Clear(&tagBuffer);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_InternalPrepare(): prepare") < 0) {
|
|
// this is needed to avoid "invalid handle" errors since Oracle doesn't
|
|
// seem to leave the pointer alone when an error is raised but the
|
|
// resulting handle is still invalid
|
|
self->handle = NULL;
|
|
return -1;
|
|
}
|
|
|
|
// clear bind variables, if applicable
|
|
if (!self->setInputSizes) {
|
|
Py_XDECREF(self->bindVariables);
|
|
self->bindVariables = NULL;
|
|
}
|
|
|
|
// clear row factory, if spplicable
|
|
Py_XDECREF(self->rowFactory);
|
|
self->rowFactory = NULL;
|
|
|
|
// determine if statement is a query
|
|
if (Cursor_GetStatementType(self) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Parse()
|
|
// Parse the statement without executing it. This also retrieves information
|
|
// about the select list for select statements.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Parse(
|
|
udt_Cursor *self, // cursor to perform parse on
|
|
PyObject *args) // arguments
|
|
{
|
|
PyObject *statement;
|
|
sword status;
|
|
ub4 mode;
|
|
|
|
// statement text is expected
|
|
if (!PyArg_ParseTuple(args, "S", &statement))
|
|
return NULL;
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// prepare the statement
|
|
if (Cursor_InternalPrepare(self, statement, NULL) < 0)
|
|
return NULL;
|
|
|
|
// parse the statement
|
|
if (self->statementType == OCI_STMT_SELECT)
|
|
mode = OCI_DESCRIBE_ONLY;
|
|
else mode = OCI_PARSE_ONLY;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
status = OCIStmtExecute(self->connection->handle, self->handle,
|
|
self->environment->errorHandle, 0, 0, 0, 0, mode);
|
|
Py_END_ALLOW_THREADS
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_Parse()") < 0)
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Prepare()
|
|
// Prepare the statement for execution.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Prepare(
|
|
udt_Cursor *self, // cursor to perform prepare on
|
|
PyObject *args) // arguments
|
|
{
|
|
PyObject *statement, *statementTag;
|
|
|
|
// statement text and optional tag is expected
|
|
statementTag = NULL;
|
|
if (!PyArg_ParseTuple(args, "O|O", &statement, &statementTag))
|
|
return NULL;
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// prepare the statement
|
|
if (Cursor_InternalPrepare(self, statement, statementTag) < 0)
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_CallCalculateSize()
|
|
// Calculate the size of the statement that is to be executed.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_CallCalculateSize(
|
|
PyObject *name, // name of procedure/function to call
|
|
udt_Variable *returnValue, // return value variable (optional)
|
|
PyObject *listOfArguments, // list of positional arguments
|
|
PyObject *keywordArguments, // dictionary of keyword arguments
|
|
int *size) // statement size (OUT)
|
|
{
|
|
int numPositionalArgs, numKeywordArgs;
|
|
|
|
// set base size without any arguments
|
|
*size = 17;
|
|
|
|
// add any additional space required to handle the return value
|
|
if (returnValue)
|
|
*size += 6;
|
|
|
|
// assume up to 9 characters for each positional argument
|
|
// this allows up to four digits for the placeholder if the bind variale
|
|
// is a boolean value
|
|
if (listOfArguments) {
|
|
numPositionalArgs = PySequence_Size(listOfArguments);
|
|
if (numPositionalArgs < 0)
|
|
return -1;
|
|
*size += numPositionalArgs * 9;
|
|
}
|
|
|
|
// assume up to 15 characters for each keyword argument
|
|
// this allows up to four digits for the placeholder if the bind variable
|
|
// is a boolean value
|
|
if (keywordArguments) {
|
|
numKeywordArgs = PyDict_Size(keywordArguments);
|
|
if (numKeywordArgs < 0)
|
|
return -1;
|
|
*size += numKeywordArgs * 15;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_CallBuildStatement()
|
|
// Determine the statement and the bind variables to bind to the statement
|
|
// that is created for calling a stored procedure or function.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_CallBuildStatement(
|
|
PyObject *name, // name of procedure/function to call
|
|
udt_Variable *returnValue, // return value variable (optional)
|
|
PyObject *listOfArguments, // arguments
|
|
PyObject *keywordArguments, // keyword arguments
|
|
char *statement, // allocated statement text
|
|
PyObject **statementObj, // statement object (OUT)
|
|
PyObject **bindVariables) // variables to bind (OUT)
|
|
{
|
|
PyObject *key, *value, *format, *formatArgs, *positionalArgs, *temp;
|
|
int i, argNum, numPositionalArgs;
|
|
Py_ssize_t pos;
|
|
char *ptr;
|
|
|
|
// initialize the bind variables to the list of positional arguments
|
|
if (listOfArguments)
|
|
*bindVariables = PySequence_List(listOfArguments);
|
|
else *bindVariables = PyList_New(0);
|
|
if (!*bindVariables)
|
|
return -1;
|
|
|
|
// insert the return variable, if applicable
|
|
if (returnValue) {
|
|
if (PyList_Insert(*bindVariables, 0, (PyObject*) returnValue) < 0)
|
|
return -1;
|
|
}
|
|
|
|
// initialize format arguments
|
|
formatArgs = PyList_New(0);
|
|
if (!formatArgs)
|
|
return -1;
|
|
if (PyList_Append(formatArgs, name) < 0) {
|
|
Py_DECREF(formatArgs);
|
|
return -1;
|
|
}
|
|
|
|
// begin building the statement
|
|
argNum = 1;
|
|
strcpy(statement, "begin ");
|
|
if (returnValue) {
|
|
strcat(statement, ":1 := ");
|
|
argNum++;
|
|
}
|
|
strcat(statement, "%s");
|
|
ptr = statement + strlen(statement);
|
|
*ptr++ = '(';
|
|
|
|
// include any positional arguments first
|
|
if (listOfArguments) {
|
|
positionalArgs = PySequence_Fast(listOfArguments,
|
|
"expecting sequence of arguments");
|
|
if (!positionalArgs) {
|
|
Py_DECREF(formatArgs);
|
|
return -1;
|
|
}
|
|
numPositionalArgs = PySequence_Size(listOfArguments);
|
|
for (i = 0; i < numPositionalArgs; i++) {
|
|
if (i > 0)
|
|
*ptr++ = ',';
|
|
ptr += sprintf(ptr, ":%d", argNum++);
|
|
if (PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i)))
|
|
ptr += sprintf(ptr, " = 1");
|
|
}
|
|
Py_DECREF(positionalArgs);
|
|
}
|
|
|
|
// next append any keyword arguments
|
|
if (keywordArguments) {
|
|
pos = 0;
|
|
while (PyDict_Next(keywordArguments, &pos, &key, &value)) {
|
|
if (PyList_Append(*bindVariables, value) < 0) {
|
|
Py_DECREF(formatArgs);
|
|
return -1;
|
|
}
|
|
if (PyList_Append(formatArgs, key) < 0) {
|
|
Py_DECREF(formatArgs);
|
|
return -1;
|
|
}
|
|
if ((argNum > 1 && !returnValue) || (argNum > 2 && returnValue))
|
|
*ptr++ = ',';
|
|
ptr += sprintf(ptr, "%%s => :%d", argNum++);
|
|
if (PyBool_Check(value))
|
|
ptr += sprintf(ptr, " = 1");
|
|
}
|
|
}
|
|
|
|
// create statement object
|
|
strcpy(ptr, "); end;");
|
|
format = cxString_FromAscii(statement);
|
|
if (!format) {
|
|
Py_DECREF(formatArgs);
|
|
return -1;
|
|
}
|
|
temp = PyList_AsTuple(formatArgs);
|
|
Py_DECREF(formatArgs);
|
|
if (!temp) {
|
|
Py_DECREF(format);
|
|
return -1;
|
|
}
|
|
*statementObj = cxString_Format(format, temp);
|
|
Py_DECREF(format);
|
|
Py_DECREF(temp);
|
|
if (!*statementObj)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Call()
|
|
// Call a stored procedure or function.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_Call(
|
|
udt_Cursor *self, // cursor to call procedure/function
|
|
udt_Variable *returnValue, // return value variable (optional)
|
|
PyObject *name, // name of procedure/function to call
|
|
PyObject *listOfArguments, // arguments
|
|
PyObject *keywordArguments) // keyword arguments
|
|
{
|
|
PyObject *bindVariables, *statementObj, *results;
|
|
int statementSize;
|
|
char *statement;
|
|
|
|
// verify that the arguments are passed correctly
|
|
if (listOfArguments) {
|
|
if (!PySequence_Check(listOfArguments)) {
|
|
PyErr_SetString(PyExc_TypeError, "arguments must be a sequence");
|
|
return -1;
|
|
}
|
|
}
|
|
if (keywordArguments) {
|
|
if (!PyDict_Check(keywordArguments)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"keyword arguments must be a dictionary");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return -1;
|
|
|
|
// determine the statement size
|
|
if (Cursor_CallCalculateSize(name, returnValue, listOfArguments,
|
|
keywordArguments, &statementSize) < 0)
|
|
return -1;
|
|
|
|
// allocate a string for the statement
|
|
statement = (char*) PyMem_Malloc(statementSize);
|
|
if (!statement) {
|
|
PyErr_NoMemory();
|
|
return -1;
|
|
}
|
|
|
|
// determine the statement to execute and the argument to pass
|
|
bindVariables = statementObj = NULL;
|
|
if (Cursor_CallBuildStatement(name, returnValue, listOfArguments,
|
|
keywordArguments, statement, &statementObj, &bindVariables) < 0) {
|
|
PyMem_Free(statement);
|
|
Py_XDECREF(statementObj);
|
|
Py_XDECREF(bindVariables);
|
|
return -1;
|
|
}
|
|
PyMem_Free(statement);
|
|
|
|
// execute the statement on the cursor
|
|
results = PyObject_CallMethod( (PyObject*) self, "execute", "OO",
|
|
statementObj, bindVariables);
|
|
Py_DECREF(statementObj);
|
|
Py_DECREF(bindVariables);
|
|
if (!results)
|
|
return -1;
|
|
Py_DECREF(results);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_CallFunc()
|
|
// Call a stored function and return the return value of the function.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_CallFunc(
|
|
udt_Cursor *self, // cursor to execute
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
static char *keywordList[] = { "name", "returnType", "parameters",
|
|
"keywordParameters", NULL };
|
|
PyObject *listOfArguments, *keywordArguments, *returnType, *results, *name;
|
|
udt_Variable *var;
|
|
|
|
// parse arguments
|
|
listOfArguments = keywordArguments = NULL;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO|OO", keywordList,
|
|
&name, &returnType, &listOfArguments, &keywordArguments))
|
|
return NULL;
|
|
|
|
// create the return variable
|
|
var = Variable_NewByType(self, returnType, 1);
|
|
if (!var)
|
|
return NULL;
|
|
|
|
// call the function
|
|
if (Cursor_Call(self, var, name, listOfArguments, keywordArguments) < 0)
|
|
return NULL;
|
|
|
|
// determine the results
|
|
results = Variable_GetValue(var, 0);
|
|
Py_DECREF(var);
|
|
return results;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_CallProc()
|
|
// Call a stored procedure and return the (possibly modified) arguments.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_CallProc(
|
|
udt_Cursor *self, // cursor to execute
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
static char *keywordList[] = { "name", "parameters", "keywordParameters",
|
|
NULL };
|
|
PyObject *listOfArguments, *keywordArguments, *results, *var, *temp, *name;
|
|
int numArgs, i;
|
|
|
|
// parse arguments
|
|
listOfArguments = keywordArguments = NULL;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
|
|
&name, &listOfArguments, &keywordArguments))
|
|
return NULL;
|
|
|
|
// call the stored procedure
|
|
if (Cursor_Call(self, NULL, name, listOfArguments, keywordArguments) < 0)
|
|
return NULL;
|
|
|
|
// create the return value
|
|
numArgs = PyList_GET_SIZE(self->bindVariables);
|
|
results = PyList_New(numArgs);
|
|
if (!results)
|
|
return NULL;
|
|
for (i = 0; i < numArgs; i++) {
|
|
var = PyList_GET_ITEM(self->bindVariables, i);
|
|
temp = Variable_GetValue((udt_Variable*) var, 0);
|
|
if (!temp) {
|
|
Py_DECREF(results);
|
|
return NULL;
|
|
}
|
|
PyList_SET_ITEM(results, i, temp);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Execute()
|
|
// Execute the statement.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Execute(
|
|
udt_Cursor *self, // cursor to execute
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keywords
|
|
{
|
|
PyObject *statement, *executeArgs;
|
|
int isQuery;
|
|
|
|
executeArgs = NULL;
|
|
if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
|
|
return NULL;
|
|
if (executeArgs && keywordArgs) {
|
|
if (PyDict_Size(keywordArgs) == 0)
|
|
keywordArgs = NULL;
|
|
else {
|
|
PyErr_SetString(g_InterfaceErrorException,
|
|
"expecting argument or keyword arguments, not both");
|
|
return NULL;
|
|
}
|
|
}
|
|
if (keywordArgs)
|
|
executeArgs = keywordArgs;
|
|
if (executeArgs) {
|
|
if (!PyDict_Check(executeArgs) && !PySequence_Check(executeArgs)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"expecting a dictionary, sequence or keyword args");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// prepare the statement, if applicable
|
|
if (Cursor_InternalPrepare(self, statement, NULL) < 0)
|
|
return NULL;
|
|
|
|
// perform binds
|
|
if (executeArgs && Cursor_SetBindVariables(self, executeArgs, 1, 0,
|
|
0) < 0)
|
|
return NULL;
|
|
if (Cursor_PerformBind(self) < 0)
|
|
return NULL;
|
|
|
|
// execute the statement
|
|
isQuery = (self->statementType == OCI_STMT_SELECT);
|
|
if (Cursor_InternalExecute(self, isQuery ? 0 : 1) < 0)
|
|
return NULL;
|
|
|
|
// perform defines, if necessary
|
|
if (isQuery && !self->fetchVariables && Cursor_PerformDefine(self) < 0)
|
|
return NULL;
|
|
|
|
// reset the values of setoutputsize()
|
|
self->outputSize = -1;
|
|
self->outputSizeColumn = -1;
|
|
|
|
// for queries, return the cursor for convenience
|
|
if (isQuery) {
|
|
Py_INCREF(self);
|
|
return (PyObject*) self;
|
|
}
|
|
|
|
// for all other statements, simply return None
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_ExecuteMany()
|
|
// Execute the statement many times. The number of times is equivalent to the
|
|
// number of elements in the array of dictionaries.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_ExecuteMany(
|
|
udt_Cursor *self, // cursor to execute
|
|
PyObject *args) // arguments
|
|
{
|
|
PyObject *arguments, *listOfArguments, *statement;
|
|
int i, numRows;
|
|
|
|
// expect statement text (optional) plus list of mappings
|
|
if (!PyArg_ParseTuple(args, "OO!", &statement, &PyList_Type,
|
|
&listOfArguments))
|
|
return NULL;
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// prepare the statement
|
|
if (Cursor_InternalPrepare(self, statement, NULL) < 0)
|
|
return NULL;
|
|
|
|
// queries are not supported as the result is undefined
|
|
if (self->statementType == OCI_STMT_SELECT) {
|
|
PyErr_SetString(g_NotSupportedErrorException,
|
|
"queries not supported: results undefined");
|
|
return NULL;
|
|
}
|
|
|
|
// perform binds
|
|
numRows = PyList_GET_SIZE(listOfArguments);
|
|
for (i = 0; i < numRows; i++) {
|
|
arguments = PyList_GET_ITEM(listOfArguments, i);
|
|
if (!PyDict_Check(arguments) && !PySequence_Check(arguments)) {
|
|
PyErr_SetString(g_InterfaceErrorException,
|
|
"expecting a list of dictionaries or sequences");
|
|
return NULL;
|
|
}
|
|
if (Cursor_SetBindVariables(self, arguments, numRows, i,
|
|
(i < numRows - 1)) < 0)
|
|
return NULL;
|
|
}
|
|
if (Cursor_PerformBind(self) < 0)
|
|
return NULL;
|
|
|
|
// execute the statement, but only if the number of rows is greater than
|
|
// zero since Oracle raises an error otherwise
|
|
if (numRows > 0) {
|
|
if (Cursor_InternalExecute(self, numRows) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_ExecuteManyPrepared()
|
|
// Execute the prepared statement the number of times requested. At this
|
|
// point, the statement must have been already prepared and the bind variables
|
|
// must have their values set.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_ExecuteManyPrepared(
|
|
udt_Cursor *self, // cursor to execute
|
|
PyObject *args) // arguments
|
|
{
|
|
int numIters;
|
|
|
|
// expect number of times to execute the statement
|
|
if (!PyArg_ParseTuple(args, "i", &numIters))
|
|
return NULL;
|
|
if (numIters > self->bindArraySize) {
|
|
PyErr_SetString(g_InterfaceErrorException,
|
|
"iterations exceed bind array size");
|
|
return NULL;
|
|
}
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// queries are not supported as the result is undefined
|
|
if (self->statementType == OCI_STMT_SELECT) {
|
|
PyErr_SetString(g_NotSupportedErrorException,
|
|
"queries not supported: results undefined");
|
|
return NULL;
|
|
}
|
|
|
|
// perform binds
|
|
if (Cursor_PerformBind(self) < 0)
|
|
return NULL;
|
|
|
|
// execute the statement
|
|
if (Cursor_InternalExecute(self, numIters) < 0)
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_VerifyFetch()
|
|
// Verify that fetching may happen from this cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_VerifyFetch(
|
|
udt_Cursor *self) // cursor to fetch from
|
|
{
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return -1;
|
|
|
|
// fixup bound cursor, if necessary
|
|
if (Cursor_FixupBoundCursor(self) < 0)
|
|
return -1;
|
|
|
|
// make sure the cursor is for a query
|
|
if (self->statementType != OCI_STMT_SELECT) {
|
|
PyErr_SetString(g_InterfaceErrorException, "not a query");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_InternalFetch()
|
|
// Performs the actual fetch from Oracle.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_InternalFetch(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
int numRows) // number of rows to fetch
|
|
{
|
|
udt_Variable *var;
|
|
sword status;
|
|
ub4 rowCount;
|
|
int i;
|
|
|
|
if (!self->fetchVariables) {
|
|
PyErr_SetString(g_InterfaceErrorException, "query not executed");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < PyList_GET_SIZE(self->fetchVariables); i++) {
|
|
var = (udt_Variable*) PyList_GET_ITEM(self->fetchVariables, i);
|
|
var->internalFetchNum++;
|
|
if (var->type->preFetchProc) {
|
|
if ((*var->type->preFetchProc)(var) < 0)
|
|
return -1;
|
|
}
|
|
}
|
|
Py_BEGIN_ALLOW_THREADS
|
|
status = OCIStmtFetch(self->handle, self->environment->errorHandle,
|
|
numRows, OCI_FETCH_NEXT, OCI_DEFAULT);
|
|
Py_END_ALLOW_THREADS
|
|
if (status != OCI_NO_DATA) {
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_InternalFetch(): fetch") < 0)
|
|
return -1;
|
|
}
|
|
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &rowCount, 0,
|
|
OCI_ATTR_ROW_COUNT, self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Cursor_InternalFetch(): row count") < 0)
|
|
return -1;
|
|
self->actualRows = rowCount - self->rowCount;
|
|
self->rowNum = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_MoreRows()
|
|
// Returns an integer indicating if more rows can be retrieved from the
|
|
// cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static int Cursor_MoreRows(
|
|
udt_Cursor *self) // cursor to fetch from
|
|
{
|
|
if (self->rowNum >= self->actualRows) {
|
|
if (self->actualRows < 0 || self->actualRows == self->fetchArraySize) {
|
|
if (Cursor_InternalFetch(self, self->fetchArraySize) < 0)
|
|
return -1;
|
|
}
|
|
if (self->rowNum >= self->actualRows)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_MultiFetch()
|
|
// Return a list consisting of the remaining rows up to the given row limit
|
|
// (if specified).
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_MultiFetch(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
int rowLimit) // row limit
|
|
{
|
|
PyObject *results, *row;
|
|
int rowNum, status;
|
|
|
|
// create an empty list
|
|
results = PyList_New(0);
|
|
if (!results)
|
|
return NULL;
|
|
|
|
// fetch as many rows as possible
|
|
for (rowNum = 0; rowLimit == 0 || rowNum < rowLimit; rowNum++) {
|
|
status = Cursor_MoreRows(self);
|
|
if (status < 0) {
|
|
Py_DECREF(results);
|
|
return NULL;
|
|
} else if (status == 0) {
|
|
break;
|
|
} else {
|
|
row = Cursor_CreateRow(self);
|
|
if (!row) {
|
|
Py_DECREF(results);
|
|
return NULL;
|
|
}
|
|
if (PyList_Append(results, row) < 0) {
|
|
Py_DECREF(row);
|
|
Py_DECREF(results);
|
|
return NULL;
|
|
}
|
|
Py_DECREF(row);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FetchOne()
|
|
// Fetch a single row from the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_FetchOne(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args) // arguments
|
|
{
|
|
int status;
|
|
|
|
// verify fetch can be performed
|
|
if (Cursor_VerifyFetch(self) < 0)
|
|
return NULL;
|
|
|
|
// setup return value
|
|
status = Cursor_MoreRows(self);
|
|
if (status < 0)
|
|
return NULL;
|
|
else if (status > 0)
|
|
return Cursor_CreateRow(self);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FetchMany()
|
|
// Fetch multiple rows from the cursor based on the arraysize.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_FetchMany(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
static char *keywordList[] = { "numRows", NULL };
|
|
int rowLimit;
|
|
|
|
// parse arguments -- optional rowlimit expected
|
|
rowLimit = self->arraySize;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
|
|
&rowLimit))
|
|
return NULL;
|
|
|
|
// verify fetch can be performed
|
|
if (Cursor_VerifyFetch(self) < 0)
|
|
return NULL;
|
|
|
|
return Cursor_MultiFetch(self, rowLimit);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FetchAll()
|
|
// Fetch all remaining rows from the cursor.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_FetchAll(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args) // arguments
|
|
{
|
|
if (Cursor_VerifyFetch(self) < 0)
|
|
return NULL;
|
|
return Cursor_MultiFetch(self, 0);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_FetchRaw()
|
|
// Perform raw fetch on the cursor; return the actual number of rows fetched.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_FetchRaw(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
static char *keywordList[] = { "numRows", NULL };
|
|
int numRowsToFetch, numRowsFetched;
|
|
|
|
// expect an optional number of rows to retrieve
|
|
numRowsToFetch = self->fetchArraySize;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
|
|
&numRowsToFetch))
|
|
return NULL;
|
|
if (numRowsToFetch > self->fetchArraySize) {
|
|
PyErr_SetString(g_InterfaceErrorException,
|
|
"rows to fetch exceeds array size");
|
|
return NULL;
|
|
}
|
|
|
|
// do not attempt to perform fetch if no more rows to fetch
|
|
if (self->actualRows > 0 && self->actualRows < self->fetchArraySize)
|
|
return PyInt_FromLong(0);
|
|
|
|
// perform internal fetch
|
|
if (Cursor_InternalFetch(self, numRowsToFetch) < 0)
|
|
return NULL;
|
|
|
|
self->rowCount += self->actualRows;
|
|
numRowsFetched = self->actualRows;
|
|
if (self->actualRows == numRowsToFetch)
|
|
self->actualRows = -1;
|
|
return PyInt_FromLong(numRowsFetched);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetInputSizes()
|
|
// Set the sizes of the bind variables.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_SetInputSizes(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
int numPositionalArgs;
|
|
PyObject *key, *value;
|
|
udt_Variable *var;
|
|
Py_ssize_t i;
|
|
|
|
// only expect keyword arguments or positional arguments, not both
|
|
numPositionalArgs = PyTuple_Size(args);
|
|
if (keywordArgs && numPositionalArgs > 0) {
|
|
PyErr_SetString(g_InterfaceErrorException,
|
|
"expecting arguments or keyword arguments, not both");
|
|
return NULL;
|
|
}
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// eliminate existing bind variables
|
|
Py_XDECREF(self->bindVariables);
|
|
if (keywordArgs)
|
|
self->bindVariables = PyDict_New();
|
|
else self->bindVariables = PyList_New(numPositionalArgs);
|
|
if (!self->bindVariables)
|
|
return NULL;
|
|
self->setInputSizes = 1;
|
|
|
|
// process each input
|
|
if (keywordArgs) {
|
|
i = 0;
|
|
while (PyDict_Next(keywordArgs, &i, &key, &value)) {
|
|
var = Variable_NewByType(self, value, self->bindArraySize);
|
|
if (!var)
|
|
return NULL;
|
|
if (PyDict_SetItem(self->bindVariables, key,
|
|
(PyObject*) var) < 0) {
|
|
Py_DECREF(var);
|
|
return NULL;
|
|
}
|
|
Py_DECREF(var);
|
|
}
|
|
} else {
|
|
for (i = 0; i < numPositionalArgs; i++) {
|
|
value = PyTuple_GET_ITEM(args, i);
|
|
if (value == Py_None) {
|
|
Py_INCREF(Py_None);
|
|
PyList_SET_ITEM(self->bindVariables, i, Py_None);
|
|
} else {
|
|
var = Variable_NewByType(self, value, self->bindArraySize);
|
|
if (!var)
|
|
return NULL;
|
|
PyList_SET_ITEM(self->bindVariables, i, (PyObject*) var);
|
|
}
|
|
}
|
|
}
|
|
|
|
Py_INCREF(self->bindVariables);
|
|
return self->bindVariables;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_SetOutputSize()
|
|
// Set the size of all of the long columns or just one of them.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_SetOutputSize(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args) // arguments
|
|
{
|
|
self->outputSizeColumn = -1;
|
|
if (!PyArg_ParseTuple(args, "i|i", &self->outputSize,
|
|
&self->outputSizeColumn))
|
|
return NULL;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_Var()
|
|
// Create a bind variable and return it.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_Var(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args, // arguments
|
|
PyObject *keywordArgs) // keyword arguments
|
|
{
|
|
static char *keywordList[] = { "type", "size", "arraysize",
|
|
"inconverter", "outconverter", "typename", NULL };
|
|
PyObject *inConverter, *outConverter, *typeNameObj;
|
|
udt_VariableType *varType;
|
|
udt_ObjectVar *objectVar;
|
|
int size, arraySize;
|
|
udt_Variable *var;
|
|
PyObject *type;
|
|
|
|
// parse arguments
|
|
size = 0;
|
|
arraySize = self->bindArraySize;
|
|
inConverter = outConverter = typeNameObj = NULL;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|iiOOO", keywordList,
|
|
&type, &size, &arraySize, &inConverter, &outConverter,
|
|
&typeNameObj))
|
|
return NULL;
|
|
|
|
// determine the type of variable
|
|
varType = Variable_TypeByPythonType(self, type);
|
|
if (!varType)
|
|
return NULL;
|
|
if (varType->isVariableLength && size == 0)
|
|
size = varType->size;
|
|
if (type == (PyObject*) &g_ObjectVarType && !typeNameObj) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"expecting type name for object variables");
|
|
return NULL;
|
|
}
|
|
|
|
// create the variable
|
|
var = Variable_New(self, arraySize, varType, size);
|
|
if (!var)
|
|
return NULL;
|
|
Py_XINCREF(inConverter);
|
|
var->inConverter = inConverter;
|
|
Py_XINCREF(outConverter);
|
|
var->outConverter = outConverter;
|
|
|
|
// define the object type if needed
|
|
if (type == (PyObject*) &g_ObjectVarType) {
|
|
objectVar = (udt_ObjectVar*) var;
|
|
objectVar->objectType = ObjectType_NewByName(self->connection,
|
|
typeNameObj);
|
|
if (!objectVar->objectType) {
|
|
Py_DECREF(var);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (PyObject*) var;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_ArrayVar()
|
|
// Create an array bind variable and return it.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_ArrayVar(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args) // arguments
|
|
{
|
|
udt_VariableType *varType;
|
|
PyObject *type, *value;
|
|
int size, numElements;
|
|
udt_Variable *var;
|
|
|
|
// parse arguments
|
|
size = 0;
|
|
if (!PyArg_ParseTuple(args, "OO|i", &type, &value, &size))
|
|
return NULL;
|
|
|
|
// determine the type of variable
|
|
varType = Variable_TypeByPythonType(self, type);
|
|
if (!varType)
|
|
return NULL;
|
|
if (varType->isVariableLength && size == 0)
|
|
size = varType->size;
|
|
|
|
// determine the number of elements to create
|
|
if (PyList_Check(value))
|
|
numElements = PyList_GET_SIZE(value);
|
|
else if (PyInt_Check(value)) {
|
|
numElements = PyInt_AsLong(value);
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
} else {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"expecting integer or list of values");
|
|
return NULL;
|
|
}
|
|
|
|
// create the variable
|
|
var = Variable_New(self, numElements, varType, size);
|
|
if (!var)
|
|
return NULL;
|
|
if (Variable_MakeArray(var) < 0) {
|
|
Py_DECREF(var);
|
|
return NULL;
|
|
}
|
|
|
|
// set the value, if applicable
|
|
if (PyList_Check(value)) {
|
|
if (Variable_SetArrayValue(var, value) < 0)
|
|
return NULL;
|
|
}
|
|
|
|
return (PyObject*) var;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_BindNames()
|
|
// Return a list of bind variable names.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_BindNames(
|
|
udt_Cursor *self, // cursor to fetch from
|
|
PyObject *args) // arguments
|
|
{
|
|
PyObject *names;
|
|
int result;
|
|
|
|
// make sure the cursor is open
|
|
if (Cursor_IsOpen(self) < 0)
|
|
return NULL;
|
|
|
|
// return result
|
|
result = Cursor_GetBindNames(self, 8, &names);
|
|
if (result < 0)
|
|
return NULL;
|
|
if (!names && Cursor_GetBindNames(self, result, &names) < 0)
|
|
return NULL;
|
|
return names;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_GetIter()
|
|
// Return a reference to the cursor which supports the iterator protocol.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_GetIter(
|
|
udt_Cursor *self) // cursor
|
|
{
|
|
if (Cursor_VerifyFetch(self) < 0)
|
|
return NULL;
|
|
Py_INCREF(self);
|
|
return (PyObject*) self;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cursor_GetNext()
|
|
// Return a reference to the cursor which supports the iterator protocol.
|
|
//-----------------------------------------------------------------------------
|
|
static PyObject *Cursor_GetNext(
|
|
udt_Cursor *self) // cursor
|
|
{
|
|
int status;
|
|
|
|
if (Cursor_VerifyFetch(self) < 0)
|
|
return NULL;
|
|
status = Cursor_MoreRows(self);
|
|
if (status < 0)
|
|
return NULL;
|
|
else if (status > 0)
|
|
return Cursor_CreateRow(self);
|
|
|
|
// no more rows, return NULL without setting an exception
|
|
return NULL;
|
|
}
|
|
|