python-cx_Oracle/Cursor.c
Anthony Tuininga d4eb90d848 The current position does not need to be checked upon execute (since we are
always moving forward) and especially not since its value is always zero in
any case!
2017-01-12 14:08:05 -07:00

2677 lines
92 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;
ub4 arraySize;
ub4 bindArraySize;
ub4 fetchArraySize;
int numbersAsStrings;
int setInputSizes;
int outputSize;
int outputSizeColumn;
ub8 rowCount;
ub4 bufferRowCount;
ub4 bufferRowIndex;
int statementType;
int hasRowsToFetch;
int isOpen;
int isOwned;
boolean isScrollable;
} 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*, 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_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*);
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*);
static PyObject* Cursor_GetBatchErrors(udt_Cursor*);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
static PyObject *Cursor_GetArrayDMLRowCounts(udt_Cursor*);
static PyObject *Cursor_GetImplicitResults(udt_Cursor*);
#endif
//-----------------------------------------------------------------------------
// 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 | METH_KEYWORDS },
{ "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 },
{ "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 },
{ "close", (PyCFunction) Cursor_Close, METH_NOARGS },
{ "getbatcherrors", (PyCFunction) Cursor_GetBatchErrors, METH_NOARGS },
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
{ "getarraydmlrowcounts", (PyCFunction) Cursor_GetArrayDMLRowCounts,
METH_NOARGS },
{ "getimplicitresults", (PyCFunction) Cursor_GetImplicitResults,
METH_NOARGS },
#endif
{ NULL, NULL }
};
//-----------------------------------------------------------------------------
// declaration of members for Python type "Cursor"
//-----------------------------------------------------------------------------
static PyMemberDef g_CursorMembers[] = {
{ "arraysize", T_UINT, offsetof(udt_Cursor, arraySize), 0 },
{ "bindarraysize", T_UINT, offsetof(udt_Cursor, bindArraySize), 0 },
{ "rowcount", T_ULONGLONG, 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 },
{ "scrollable", T_BOOL, offsetof(udt_Cursor, isScrollable), 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)
"cx_Oracle.Cursor", // 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
{
static char *keywordList[] = { "connection", "scrollable", NULL };
udt_Connection *connection;
PyObject *scrollableObj;
// parse arguments
scrollableObj = NULL;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!|O", keywordList,
&g_ConnectionType, &connection, &scrollableObj))
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;
if (scrollableObj) {
self->isScrollable = PyObject_IsTrue(scrollableObj);
if (self->isScrollable < 0)
return -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_GetRowCount()
// Get the row count from the statement handle. This is a 64-bit value in
// Oracle Database 12.1 and later and a 32-bit value prior to that.
//-----------------------------------------------------------------------------
static int Cursor_GetRowCount(
udt_Cursor *self, // cursor to get the row count on
ub8* value) // the row count (OUT)
{
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
ub8 rowCount;
ub4 attribute = OCI_ATTR_UB8_ROW_COUNT;
#else
ub4 rowCount;
ub4 attribute = OCI_ATTR_ROW_COUNT;
#endif
sword status;
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &rowCount, 0,
attribute, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetRowCount()") < 0)
return -1;
*value = rowCount;
return 0;
}
//-----------------------------------------------------------------------------
// Cursor_SetRowCount()
// Set the rowcount variable.
//-----------------------------------------------------------------------------
static int Cursor_SetRowCount(
udt_Cursor *self) // cursor to set the rowcount on
{
self->rowCount = 0;
self->hasRowsToFetch = 0;
if (self->statementType == OCI_STMT_SELECT) {
self->bufferRowCount = 0;
self->bufferRowIndex = self->fetchArraySize;
self->hasRowsToFetch = 1;
} else if (self->statementType == OCI_STMT_INSERT ||
self->statementType == OCI_STMT_UPDATE ||
self->statementType == OCI_STMT_DELETE ||
self->statementType == OCI_STMT_BEGIN ||
self->statementType == OCI_STMT_DECLARE) {
if (Cursor_GetRowCount(self, &self->rowCount) < 0)
return -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, *args;
udt_Error *error;
ub2 offset = 0;
PyErr_Fetch(&type, &value, &traceback);
if (type == g_DatabaseErrorException) {
PyErr_NormalizeException(&type, &value, &traceback);
OCIAttrGet(self->handle, OCI_HTYPE_STMT, &offset, 0,
OCI_ATTR_PARSE_ERROR_OFFSET, self->environment->errorHandle);
args = PyObject_GetAttrString(value, "args");
error = (udt_Error*) PyTuple_GET_ITEM(args, 0);
error->offset = offset;
Py_DECREF(args);
}
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
ub4 additionalMode) // additional modes to set
{
sword status;
ub4 mode;
if (self->connection->autocommit)
mode = OCI_COMMIT_ON_SUCCESS;
else mode = OCI_DEFAULT;
mode |= additionalMode;
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;
else if (type == (PyObject*) &g_NCharVarType)
displaySize = charSize;
else if (type == (PyObject*) &g_BinaryVarType)
displaySize = internalSize;
else if (type == (PyObject*) &g_FixedCharVarType)
displaySize = charSize;
else if (type == (PyObject*) &g_FixedNCharVarType)
displaySize = charSize;
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**) &param, 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->bufferRowIndex);
if (!item) {
Py_DECREF(tuple);
return NULL;
}
PyTuple_SET_ITEM(tuple, pos, item);
}
// increment row counters
self->bufferRowIndex++;
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 (prior to Oracle 12.1)
numPositionalArgs = 0;
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 (prior to Oracle 12.1)
numKeywordArgs = 0;
if (keywordArguments) {
numKeywordArgs = PyDict_Size(keywordArguments);
if (numKeywordArgs < 0)
return -1;
*size += numKeywordArgs * 15;
}
// the above assume a maximum of 10,000 arguments; check and raise an
// error if the number of arguments exceeds this value; more than this
// number would probably be unusable in any case!
if (numPositionalArgs + numKeywordArgs > 10000) {
PyErr_SetString(g_InterfaceErrorException, "too many arguments");
return -1;
}
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 ORACLE_VERSION_HEX < ORACLE_VERSION(12, 1)
if (PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i)))
ptr += sprintf(ptr, " = 1");
#endif
}
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 ORACLE_VERSION_HEX < ORACLE_VERSION(12, 1)
if (PyBool_Check(value))
ptr += sprintf(ptr, " = 1");
#endif
}
}
// 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;
ub4 mode;
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
mode = OCI_DEFAULT;
isQuery = (self->statementType == OCI_STMT_SELECT);
if (isQuery && self->isScrollable)
mode = OCI_STMT_SCROLLABLE_READONLY;
if (Cursor_InternalExecute(self, isQuery ? 0 : 1, mode) < 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 *keywordArgs) // keyword arguments
{
static char *keywordList[] = { "statement", "parameters", "batcherrors",
"arraydmlrowcounts", NULL };
int i, numRows, arrayDMLRowCountsEnabled = 0, batchErrorsEnabled = 0;
PyObject *arguments, *listOfArguments, *statement;
ub4 additionalMode = 0;
// expect statement text (optional) plus list of sequences/mappings
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!|ii", keywordList,
&statement, &PyList_Type, &listOfArguments,
&batchErrorsEnabled, &arrayDMLRowCountsEnabled))
return NULL;
// make sure the cursor is open
if (Cursor_IsOpen(self) < 0)
return NULL;
// define additional mode, if needed
if (batchErrorsEnabled)
additionalMode |= OCI_BATCH_ERRORS;
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
if (arrayDMLRowCountsEnabled)
additionalMode |= OCI_RETURN_ROW_COUNT_ARRAY;
#endif
// 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, additionalMode) < 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
{
ub4 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) < 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;
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 = OCIStmtFetch2(self->handle, self->environment->errorHandle,
numRows, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
Py_END_ALLOW_THREADS
if (status == OCI_NO_DATA)
self->hasRowsToFetch = 0;
else if (Environment_CheckForError(self->environment, status,
"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;
// reset buffer row index
self->bufferRowIndex = 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->bufferRowIndex >= self->bufferRowCount) {
if (self->hasRowsToFetch) {
if (Cursor_InternalFetch(self, self->fetchArraySize) < 0)
return -1;
}
if (self->bufferRowIndex >= self->bufferRowCount)
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 };
ub4 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->bufferRowCount > 0 && self->bufferRowCount < self->fetchArraySize)
return PyInt_FromLong(0);
// perform internal fetch
if (Cursor_InternalFetch(self, numRowsToFetch) < 0)
return NULL;
self->rowCount += self->bufferRowCount;
numRowsFetched = self->bufferRowCount;
if (self->bufferRowCount == numRowsToFetch)
self->bufferRowCount = 0;
return PyInt_FromLong(numRowsFetched);
}
//-----------------------------------------------------------------------------
// 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, &currentPosition, 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.
//-----------------------------------------------------------------------------
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;
if (ObjectVar_SetType(objectVar, typeNameObj) < 0) {
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;
}
//-----------------------------------------------------------------------------
// Cursor_GetBatchErrorsHelper()
// Populates the list with the error objects.
//-----------------------------------------------------------------------------
static int Cursor_GetBatchErrorsHelper(
udt_Cursor *self, // cursor object
PyObject *listObj, // list object to populate
ub4 numBatchErrors, // number of batch errors
OCIError *errorHandle, // first error handle
OCIError *localErrorHandle) // second (local) error handle
{
udt_Error *errorObj;
sb4 rowOffset;
sword status;
ub4 i;
for (i = 0; i < numBatchErrors; i++) {
// fetch batch error for iteration
status = OCIParamGet(self->environment->errorHandle, OCI_HTYPE_ERROR,
errorHandle, (void **) &localErrorHandle, i);
if (Error_Check(self->environment, status,
"Cursor_GetBatchErrorsHelper(): get parameter",
errorHandle) < 0)
return -1;
// determine row offset
status = OCIAttrGet(localErrorHandle, OCI_HTYPE_ERROR, &rowOffset, 0,
OCI_ATTR_DML_ROW_OFFSET, errorHandle);
if (Error_Check(self->environment, status,
"Cursor_GetBatchErrorsHelper(): get row offset",
errorHandle) < 0)
return -1;
// determine error object
errorObj = Error_InternalNew(self->environment, "Batch Error",
OCI_HTYPE_ERROR, localErrorHandle);
if (!errorObj)
return -1;
errorObj->offset = rowOffset;
// populate list
PyList_SET_ITEM(listObj, i, (PyObject*) errorObj);
}
return 0;
}
//-----------------------------------------------------------------------------
// Cursor_GetBatchErrors()
// Returns a list of batch error objects.
//-----------------------------------------------------------------------------
static PyObject* Cursor_GetBatchErrors(
udt_Cursor *self)
{
OCIError *errorHandle, *localErrorHandle;
ub4 numBatchErrors;
PyObject *result;
sword status;
// determine the number of errors
status = OCIAttrGet(self->handle, (ub4) OCI_HTYPE_STMT,
(ub4 *) &numBatchErrors, 0, OCI_ATTR_NUM_DML_ERRORS,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetBatchErrors(): get number of errors") < 0)
return NULL;
// create list and if no errors, simply return immediately
result = PyList_New(numBatchErrors);
if (!result)
return NULL;
if (numBatchErrors == 0)
return result;
// allocate first error handle
status = OCIHandleAlloc((void *) self->environment->handle,
(void **) &errorHandle, (ub4) OCI_HTYPE_ERROR, 0, (void *) 0);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetBatchErrors(): allocate first error handle") < 0) {
Py_DECREF(result);
return NULL;
}
// allocate second (local) error handle
status = OCIHandleAlloc((void *) self->environment->handle,
(void **) &localErrorHandle, (ub4) OCI_HTYPE_ERROR, 0, (void *) 0);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetBatchErrors(): allocate second error handle") < 0) {
Py_DECREF(result);
OCIHandleFree(errorHandle, OCI_HTYPE_ERROR);
return NULL;
}
// create error objects
if (Cursor_GetBatchErrorsHelper(self, result, numBatchErrors, errorHandle,
localErrorHandle) < 0) {
Py_DECREF(result);
result = NULL;
}
// cleanup and return result
OCIHandleFree(errorHandle, OCI_HTYPE_ERROR);
OCIHandleFree(localErrorHandle, OCI_HTYPE_ERROR);
return result;
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
//-----------------------------------------------------------------------------
// Cursor_GetArrayDMLRowCounts
// Populates the array dml row count list. Raises error for failure.
//-----------------------------------------------------------------------------
static PyObject* Cursor_GetArrayDMLRowCounts(
udt_Cursor *self) // cursor object
{
PyObject *result, *element;
ub4 rowCountArraySize, i;
ub8 *arrayDMLRowCount;
sword status;
// get number of iterations
status = OCIAttrGet(self->handle, (ub4) OCI_HTYPE_STMT,
(ub8 *) &arrayDMLRowCount, &rowCountArraySize,
OCI_ATTR_DML_ROW_COUNT_ARRAY, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetArrayDMLRowCounts(): row count") < 0)
return NULL;
// return array
result = PyList_New(rowCountArraySize);
if (!result)
return NULL;
for (i = 0; i < rowCountArraySize; i++) {
element = PyLong_FromUnsignedLong(arrayDMLRowCount[i]);
if (!element) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, i, element);
}
return result;
}
//-----------------------------------------------------------------------------
// Cursor_GetImplicitResults
// Return a list of cursors available implicitly after execution of a PL/SQL
// block or stored procedure. If none are available, an empty list is returned.
//-----------------------------------------------------------------------------
static PyObject * Cursor_GetImplicitResults(
udt_Cursor *self) // cursor object
{
ub4 i, numImplicitResults, returnType;
udt_Cursor *childCursor;
PyObject *result;
sword status;
// make sure the cursor is open
if (Cursor_IsOpen(self) < 0)
return NULL;
// make sure we have a statement executed (handle defined)
if (!self->handle) {
PyErr_SetString(g_InterfaceErrorException, "no statement executed");
return NULL;
}
// determine the number of implicit results that are available
status = OCIAttrGet(self->handle, OCI_HTYPE_STMT, &numImplicitResults, 0,
OCI_ATTR_IMPLICIT_RESULT_COUNT, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetImplicitResults(): get number of implicit results") < 0)
return NULL;
// create the list
result = PyList_New(numImplicitResults);
if (!result)
return NULL;
// populate it with the implicit results
for (i = 0; i < numImplicitResults; i++) {
childCursor = (udt_Cursor*) Connection_NewCursor(self->connection,
NULL, NULL);
if (!childCursor) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, i, (PyObject*) childCursor);
status = OCIStmtGetNextResult(self->handle,
self->environment->errorHandle, (dvoid**) &childCursor->handle,
&returnType, OCI_DEFAULT);
if (Environment_CheckForError(self->environment, status,
"Cursor_GetImplicitResults(): get next result") < 0) {
Py_DECREF(result);
return NULL;
}
if (returnType != OCI_RESULT_TYPE_SELECT) {
PyErr_SetString(g_InternalErrorException,
"Cursor_GetImplicitResults(): unexpected result type");
Py_DECREF(result);
return NULL;
}
}
return result;
}
#endif