From 104e11b67a0c0640a7355a52fcddaf73310e0982 Mon Sep 17 00:00:00 2001 From: Anthony Tuininga Date: Wed, 13 Jun 2007 21:15:16 +0000 Subject: [PATCH] Last public release from Computronix. --- Callback.c | 328 +++++++ Connection.c | 1113 +++++++++++++++++++++++ Cursor.c | 2001 +++++++++++++++++++++++++++++++++++++++++ CursorVar.c | 172 ++++ DateTimeVar.c | 222 +++++ Environment.c | 197 ++++ Error.c | 129 +++ ExternalDateTimeVar.c | 239 +++++ ExternalLobVar.c | 518 +++++++++++ ExternalObjectVar.c | 286 ++++++ HISTORY.txt | 447 +++++++++ LICENSE.txt | 35 + LobVar.c | 333 +++++++ LongVar.c | 171 ++++ MANIFEST | 66 ++ NumberVar.c | 508 +++++++++++ ObjectType.c | 518 +++++++++++ ObjectVar.c | 227 +++++ PKG-INFO | 11 + README.txt | 57 ++ SessionPool.c | 483 ++++++++++ StringVar.c | 323 +++++++ TimestampVar.c | 238 +++++ Transforms.c | 55 ++ Variable.c | 1204 +++++++++++++++++++++++++ cx_Oracle.c | 472 ++++++++++ html/about.html | 112 +++ html/blank.png | Bin 0 -> 1031 bytes html/connobj.html | 323 +++++++ html/contents.html | 109 +++ html/contents.png | Bin 0 -> 649 bytes html/cursorobj.html | 459 ++++++++++ html/cx_Oracle.css | 243 +++++ html/cx_Oracle.html | 119 +++ html/dateobj.html | 148 +++ html/front.html | 120 +++ html/index.html | 119 +++ html/index.png | Bin 0 -> 529 bytes html/lobobj.html | 169 ++++ html/module.html | 256 ++++++ html/modules.png | Bin 0 -> 598 bytes html/next.png | Bin 0 -> 511 bytes html/node12.html | 146 +++ html/node4.html | 427 +++++++++ html/node5.html | 166 ++++ html/previous.png | Bin 0 -> 511 bytes html/sesspool.html | 206 +++++ html/up.png | Bin 0 -> 577 bytes html/varobj.html | 129 +++ setup.cfg | 3 + setup.py | 114 +++ test/Connection.py | 116 +++ test/Cursor.py | 225 +++++ test/CursorVar.py | 50 + test/DateTimeVar.py | 230 +++++ test/LobVar.py | 123 +++ test/LongVar.py | 91 ++ test/NumberVar.py | 273 ++++++ test/ObjectVar.py | 38 + test/SessionPool.py | 80 ++ test/SetupTest.sql | 411 +++++++++ test/SetupTest_9i.sql | 30 + test/StringVar.py | 290 ++++++ test/TestEnv.py | 17 + test/TimestampVar.py | 130 +++ test/test.py | 67 ++ test/test_dbapi20.py | 39 + 67 files changed, 15931 insertions(+) create mode 100644 Callback.c create mode 100644 Connection.c create mode 100644 Cursor.c create mode 100644 CursorVar.c create mode 100644 DateTimeVar.c create mode 100644 Environment.c create mode 100644 Error.c create mode 100644 ExternalDateTimeVar.c create mode 100644 ExternalLobVar.c create mode 100644 ExternalObjectVar.c create mode 100644 HISTORY.txt create mode 100644 LICENSE.txt create mode 100644 LobVar.c create mode 100644 LongVar.c create mode 100644 MANIFEST create mode 100644 NumberVar.c create mode 100644 ObjectType.c create mode 100644 ObjectVar.c create mode 100644 PKG-INFO create mode 100644 README.txt create mode 100644 SessionPool.c create mode 100644 StringVar.c create mode 100644 TimestampVar.c create mode 100644 Transforms.c create mode 100644 Variable.c create mode 100644 cx_Oracle.c create mode 100644 html/about.html create mode 100644 html/blank.png create mode 100644 html/connobj.html create mode 100644 html/contents.html create mode 100644 html/contents.png create mode 100644 html/cursorobj.html create mode 100644 html/cx_Oracle.css create mode 100644 html/cx_Oracle.html create mode 100644 html/dateobj.html create mode 100644 html/front.html create mode 100644 html/index.html create mode 100644 html/index.png create mode 100644 html/lobobj.html create mode 100644 html/module.html create mode 100644 html/modules.png create mode 100644 html/next.png create mode 100644 html/node12.html create mode 100644 html/node4.html create mode 100644 html/node5.html create mode 100644 html/previous.png create mode 100644 html/sesspool.html create mode 100644 html/up.png create mode 100644 html/varobj.html create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test/Connection.py create mode 100644 test/Cursor.py create mode 100644 test/CursorVar.py create mode 100644 test/DateTimeVar.py create mode 100644 test/LobVar.py create mode 100644 test/LongVar.py create mode 100644 test/NumberVar.py create mode 100644 test/ObjectVar.py create mode 100644 test/SessionPool.py create mode 100644 test/SetupTest.sql create mode 100644 test/SetupTest_9i.sql create mode 100644 test/StringVar.py create mode 100644 test/TestEnv.py create mode 100644 test/TimestampVar.py create mode 100644 test/test.py create mode 100644 test/test_dbapi20.py diff --git a/Callback.c b/Callback.c new file mode 100644 index 0000000..0507ddb --- /dev/null +++ b/Callback.c @@ -0,0 +1,328 @@ +//----------------------------------------------------------------------------- +// Callback.c +// Definition of OCI callback functions. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Callback_NewVariable() +// Return a new variable from a callback. +//----------------------------------------------------------------------------- +static udt_Variable *Callback_NewVariable( + udt_Connection *connection, // connection to use + ub2 oracleType, // Oracle type of data + ub4 maxLength, // maximum length of elements + void *data, // data pointer + void *indicator, // indicator pointer + ub2 *returnCode, // return code pointer + ub2 *actualLength) // actual length pointer +{ + udt_VariableType *type; + udt_Variable *var; + + // determine the type to use + type = Variable_TypeByOracleDataType(oracleType, SQLCS_IMPLICIT); + if (!type) + return NULL; + + // attempt to allocate the object + var = PyObject_NEW(udt_Variable, type->pythonType); + if (!var) + return NULL; + + // perform basic initialization + // note that the number of allocated elements is set arbitrarily high + // because the OCI doesn't give information about how many elements are + // actually allocated; that has to be implied by the number of rows + // passed to OCIStmtFetch and OCIStmtExecute + Py_INCREF(connection->environment); + var->environment = connection->environment; + var->boundCursorHandle = NULL; + var->bindHandle = NULL; + var->defineHandle = NULL; + var->boundName = NULL; + var->allocatedElements = 2147483647; + var->actualElements = 0; + var->isArray = 0; + var->isAllocatedInternally = 0; + var->type = type; + var->indicator = indicator; + var->data = data; + var->actualLength = actualLength; + var->returnCode = returnCode; + var->maxLength = type->elementLength; + if (type->isVariableLength) + var->maxLength = maxLength; + + return var; +} + + +//----------------------------------------------------------------------------- +// Callback_BindByNameArgs() +// Return the arguments to be passed when OCIBindByName is called. +//----------------------------------------------------------------------------- +static PyObject *Callback_BindByNameArgs( + udt_Connection *connection, // connection to use + va_list args) // arguments to OCI function +{ + ub4 nameLength, allocatedElements, *actualElements; + ub2 dataType, *actualLength, *returnCode; + dvoid *indicator, *value; + OCIBind **bindHandlePtr; + OCIError *errorHandle; + udt_Variable *var; + PyObject *result; + sb4 valueLength; + OCIStmt *handle; + text *name; + + handle = va_arg(args, OCIStmt*); + bindHandlePtr = va_arg(args, OCIBind**); + errorHandle = va_arg(args, OCIError*); + name = va_arg(args, text*); + nameLength = va_arg(args, ub4); + value = va_arg(args, dvoid*); + valueLength = va_arg(args, sb4); + dataType = va_arg(args, int); + indicator = va_arg(args, dvoid*); + actualLength = va_arg(args, ub2*); + returnCode = va_arg(args, ub2*); + allocatedElements = va_arg(args, ub4); + actualElements = va_arg(args, ub4*); + + var = Callback_NewVariable(connection, dataType, valueLength, value, + indicator, returnCode, actualLength); + if (!var) + return NULL; + if (allocatedElements > 0) { + var->isArray = 1; + var->actualElements = *actualElements; + } + + result = Py_BuildValue("ls#O", handle, name, nameLength, var); + Py_DECREF(var); + return result; +} + + +//----------------------------------------------------------------------------- +// Callback_DefineByPosArgs() +// Return the arguments to be passed when OCIDefineByPos is called. +//----------------------------------------------------------------------------- +static PyObject *Callback_DefineByPosArgs( + udt_Connection *connection, // connection to use + va_list args) // arguments to OCI function +{ + ub2 dataType, *actualLength, *returnCode; + OCIDefine **defineHandle; + dvoid *indicator, *value; + OCIError *errorHandle; + udt_Variable *var; + PyObject *result; + OCIStmt *handle; + sb4 valueLength; + ub4 position; + + handle = va_arg(args, OCIStmt*); + defineHandle = va_arg(args, OCIDefine**); + errorHandle = va_arg(args, OCIError*); + position = va_arg(args, ub4); + value = va_arg(args, dvoid*); + valueLength = va_arg(args, sb4); + dataType = va_arg(args, int); + indicator = va_arg(args, dvoid*); + actualLength = va_arg(args, ub2*); + returnCode = va_arg(args, ub2*); + + // create a variable + var = Callback_NewVariable(connection, dataType, valueLength, value, + indicator, returnCode, actualLength); + if (!var) + return NULL; + + result = Py_BuildValue("liO", handle, position, var); + Py_DECREF(var); + return result; +} + + +//----------------------------------------------------------------------------- +// Callback_ExecuteArgs() +// Return the arguments to be passed when OCIStmtExecute is called. +//----------------------------------------------------------------------------- +static PyObject *Callback_ExecuteArgs( + va_list args) // arguments to OCI function +{ + OCISvcCtx* serviceContextHandle; + OCIError *errorHandle; + ub4 iters, rowoff; + OCIStmt *handle; + + serviceContextHandle = va_arg(args, OCISvcCtx*); + handle = va_arg(args, OCIStmt*); + errorHandle = va_arg(args, OCIError*); + iters = va_arg(args, ub4); + rowoff = va_arg(args, ub4); + + return Py_BuildValue("lii", handle, iters, rowoff); +} + + +//----------------------------------------------------------------------------- +// Callback_FetchArgs() +// Return the arguments to be passed when OCIStmtFetch is called. +//----------------------------------------------------------------------------- +static PyObject *Callback_FetchArgs( + udt_Connection *connection, // connection to use + va_list args) // arguments to OCI function +{ + ub4 numRows, rowCount; + OCIError *errorHandle; + OCIStmt *handle; + sword status; + + handle = va_arg(args, OCIStmt*); + errorHandle = va_arg(args, OCIError*); + numRows = va_arg(args, ub4); + + status = OCIAttrGet(handle, OCI_HTYPE_STMT, &rowCount, 0, + OCI_ATTR_ROW_COUNT, connection->environment->errorHandle); + if (Environment_CheckForError(connection->environment, status, + "Callback_FetchArgs()") < 0) + return NULL; + + return Py_BuildValue("lii", handle, numRows, rowCount); +} + + +//----------------------------------------------------------------------------- +// Callback_PrepareArgs() +// Return the arguments to be passed when OCIStmtPrepare is called. +//----------------------------------------------------------------------------- +static PyObject *Callback_PrepareArgs( + va_list args) // arguments to OCI function +{ + OCIError *errorHandle; + ub4 statementLength; + OCIStmt *handle; + text *statement; + + handle = va_arg(args, OCIStmt*); + errorHandle = va_arg(args, OCIError*); + statement = va_arg(args, text *); + statementLength = va_arg(args, ub4); + + return Py_BuildValue("ls#", handle, statement, statementLength); +} + + +//----------------------------------------------------------------------------- +// Callback_GetArgs() +// Return the arguments to be passed to the Python callback method. +//----------------------------------------------------------------------------- +static PyObject *Callback_GetArgs( + udt_Connection *connection, // connection to use + ub4 functionCode, // function code + va_list args) // OCI function arguments +{ + switch (functionCode) { + case OCI_FNCODE_BINDBYNAME: + return Callback_BindByNameArgs(connection, args); + case OCI_FNCODE_DEFINEBYPOS: + return Callback_DefineByPosArgs(connection, args); + case OCI_FNCODE_STMTEXECUTE: + return Callback_ExecuteArgs(args); + case OCI_FNCODE_STMTFETCH: + return Callback_FetchArgs(connection, args); + case OCI_FNCODE_STMTPREPARE: + return Callback_PrepareArgs(args); + } + + return PyTuple_New(0); +} + + +//----------------------------------------------------------------------------- +// Callback_Call() +// Actually make the call to the Python function. +//----------------------------------------------------------------------------- +static sword Callback_Call( + PyObject *tuple, // tuple containing connection/callback + ub4 functionCode, // function code + va_list args) // arguments +{ + PyObject *callback, *callbackArgs, *result; + udt_Connection *connection; + + // determine the connection and callback + connection = (udt_Connection*) PyTuple_GET_ITEM(tuple, 0); + callback = PyTuple_GET_ITEM(tuple, 1); + + // determine the arguments to pass to the function + callbackArgs = Callback_GetArgs(connection, functionCode, args); + if (!callbackArgs) + return OCI_ERROR; + + // actually make the call to the method + result = PyEval_CallObject(callback, callbackArgs); + Py_DECREF(callbackArgs); + if (!result) + return OCI_ERROR; + + Py_DECREF(result); + return OCI_SUCCESS; +} + + +//----------------------------------------------------------------------------- +// Callback_Handler() +// Callback handler for calling Python code within an OCI callback. +//----------------------------------------------------------------------------- +static sword Callback_Handler( + PyObject *tuple, // tuple containing connection/callback + dvoid *handle, // pointer to handle + ub4 handleType, // handle type + ub4 functionCode, // function code + ub1 when, // when being called + sword returnCode, // return code + ub4 *errorCode, // error code (IN/OUT) + va_list args) // arguments +{ +#ifdef WITH_THREAD + PyThreadState *threadState; +#endif + sword result; + + // create new thread state, if necessary +#ifdef WITH_THREAD + threadState = PyThreadState_Swap(NULL); + if (threadState) { + PyThreadState_Swap(threadState); + threadState = NULL; + } else { + threadState = PyThreadState_New(g_InterpreterState); + if (!threadState) { + PyErr_Print(); + return OCI_ERROR; + } + PyEval_AcquireThread(threadState); + } +#endif + + // perform the call + result = Callback_Call(tuple, functionCode, args); + if (result != OCI_CONTINUE) + PyErr_Print(); + + // restore thread state, if necessary +#ifdef WITH_THREAD + if (threadState) { + PyThreadState_Clear(threadState); + PyEval_ReleaseThread(threadState); + PyThreadState_Delete(threadState); + } +#endif + + return result; +} + diff --git a/Connection.c b/Connection.c new file mode 100644 index 0000000..78ee185 --- /dev/null +++ b/Connection.c @@ -0,0 +1,1113 @@ +//----------------------------------------------------------------------------- +// Connection.c +// Definition of the Python type OracleConnection. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// structure for the Python type "Connection" +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + OCISvcCtx *handle; + OCIServer *serverHandle; + OCISession *sessionHandle; + udt_Environment *environment; +#ifdef ORACLE_9I + udt_SessionPool *sessionPool; +#endif + PyObject *username; + PyObject *password; + PyObject *dsn; + PyObject *version; + ub4 commitMode; +} udt_Connection; + + +//----------------------------------------------------------------------------- +// constants for the OCI attributes +//----------------------------------------------------------------------------- +#ifdef ORACLE_10G +static ub4 gc_ModuleAttribute = OCI_ATTR_MODULE; +static ub4 gc_ActionAttribute = OCI_ATTR_ACTION; +static ub4 gc_ClientInfoAttribute = OCI_ATTR_CLIENT_INFO; +#endif + + +//----------------------------------------------------------------------------- +// functions for the Python type "Connection" +//----------------------------------------------------------------------------- +static void Connection_Free(udt_Connection*); +static PyObject *Connection_New(PyTypeObject*, PyObject*, PyObject*); +static int Connection_Init(udt_Connection*, PyObject*, PyObject*); +static PyObject *Connection_Repr(udt_Connection*); +static PyObject *Connection_Close(udt_Connection*, PyObject*); +static PyObject *Connection_Commit(udt_Connection*, PyObject*); +static PyObject *Connection_Begin(udt_Connection*, PyObject*); +static PyObject *Connection_Prepare(udt_Connection*, PyObject*); +static PyObject *Connection_Rollback(udt_Connection*, PyObject*); +static PyObject *Connection_NewCursor(udt_Connection*, PyObject*); +static PyObject *Connection_Cancel(udt_Connection*, PyObject*); +static PyObject *Connection_RegisterCallback(udt_Connection*, PyObject*); +static PyObject *Connection_UnregisterCallback(udt_Connection*, PyObject*); +static PyObject *Connection_GetVersion(udt_Connection*, void*); +static PyObject *Connection_GetMaxBytesPerCharacter(udt_Connection*, void*); +#ifdef OCI_NLS_CHARSET_MAXBYTESZ +static PyObject *Connection_GetEncoding(udt_Connection*, void*); +static PyObject *Connection_GetNationalEncoding(udt_Connection*, void*); +#endif +#ifdef ORACLE_10G +static int Connection_SetOCIAttr(udt_Connection*, PyObject*, ub4*); +#endif + + +//----------------------------------------------------------------------------- +// declaration of methods for Python type "Connection" +//----------------------------------------------------------------------------- +static PyMethodDef g_ConnectionMethods[] = { + { "cursor", (PyCFunction) Connection_NewCursor, METH_NOARGS }, + { "commit", (PyCFunction) Connection_Commit, METH_NOARGS }, + { "rollback", (PyCFunction) Connection_Rollback, METH_NOARGS }, + { "begin", (PyCFunction) Connection_Begin, METH_VARARGS }, + { "prepare", (PyCFunction) Connection_Prepare, METH_NOARGS }, + { "close", (PyCFunction) Connection_Close, METH_NOARGS }, + { "cancel", (PyCFunction) Connection_Cancel, METH_NOARGS }, + { "register", (PyCFunction) Connection_RegisterCallback, METH_VARARGS }, + { "unregister", (PyCFunction) Connection_UnregisterCallback, METH_VARARGS }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of members for Python type "Connection" +//----------------------------------------------------------------------------- +static PyMemberDef g_ConnectionMembers[] = { + { "username", T_OBJECT, offsetof(udt_Connection, username), READONLY }, + { "password", T_OBJECT, offsetof(udt_Connection, password), READONLY }, + { "dsn", T_OBJECT, offsetof(udt_Connection, dsn), READONLY }, + { "tnsentry", T_OBJECT, offsetof(udt_Connection, dsn), READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of calculated members for Python type "Connection" +//----------------------------------------------------------------------------- +static PyGetSetDef g_ConnectionCalcMembers[] = { +#ifdef OCI_NLS_CHARSET_MAXBYTESZ + { "encoding", (getter) Connection_GetEncoding, 0, 0, 0 }, + { "nencoding", (getter) Connection_GetNationalEncoding, 0, 0, 0 }, +#endif + { "version", (getter) Connection_GetVersion, 0, 0, 0 }, + { "maxBytesPerCharacter", (getter) Connection_GetMaxBytesPerCharacter, + 0, 0, 0 }, +#ifdef ORACLE_10G + { "module", 0, (setter) Connection_SetOCIAttr, 0, &gc_ModuleAttribute }, + { "action", 0, (setter) Connection_SetOCIAttr, 0, &gc_ActionAttribute }, + { "clientinfo", 0, (setter) Connection_SetOCIAttr, 0, + &gc_ClientInfoAttribute }, +#endif + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of Python type "Connection" +//----------------------------------------------------------------------------- +static PyTypeObject g_ConnectionType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.Connection", // tp_name + sizeof(udt_Connection), // tp_basicsize + 0, // tp_itemsize + (destructor) Connection_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Connection_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 + 0, // tp_iter + 0, // tp_iternext + g_ConnectionMethods, // tp_methods + g_ConnectionMembers, // tp_members + g_ConnectionCalcMembers, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc) Connection_Init, // tp_init + 0, // tp_alloc + (newfunc) Connection_New, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0 // tp_bases +}; + + +//----------------------------------------------------------------------------- +// Connection_IsConnected() +// Determines if the connection object is connected to the database. If not, +// a Python exception is raised. +//----------------------------------------------------------------------------- +static int Connection_IsConnected( + udt_Connection *self) // connection to check +{ + if (!self->handle) { + PyErr_SetString(g_InterfaceErrorException, "not connected"); + return -1; + } + return 0; +} + + +#ifdef ORACLE_9I +//----------------------------------------------------------------------------- +// Connection_Acquire() +// Acquire a connection from a session pool. +//----------------------------------------------------------------------------- +static int Connection_Acquire( + udt_Connection *self, // connection + udt_SessionPool *pool) // pool to acquire connection from +{ + boolean found; + sword status; + + // acquire the session from the pool + Py_BEGIN_ALLOW_THREADS + status = OCISessionGet(pool->environment->handle, + pool->environment->errorHandle, &self->handle, NULL, + (OraText*) PyString_AS_STRING(pool->name), + PyString_GET_SIZE(pool->name), NULL, 0, NULL, NULL, &found, + OCI_SESSGET_SPOOL); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(pool->environment, status, + "Connection_Acquire()") < 0) + return -1; + + // initialize members + Py_INCREF(pool); + self->sessionPool = pool; + Py_INCREF(pool->username); + self->username = pool->username; + Py_INCREF(pool->password); + self->password = pool->password; + Py_INCREF(pool->dsn); + self->dsn = pool->dsn; + + return 0; +} +#endif + + +#ifdef ORACLE_10G +//----------------------------------------------------------------------------- +// Connection_SetOCIAttr() +// Set the value of the OCI attribute. +//----------------------------------------------------------------------------- +static int Connection_SetOCIAttr( + udt_Connection *self, // connection to set + PyObject *value, // value to set + ub4 *attribute) // OCI attribute type +{ + sword status; + + // make sure connection is connected + if (Connection_IsConnected(self) < 0) + return -1; + + // set the value in the OCI + if (!PyString_Check(value)) { + PyErr_SetString(PyExc_TypeError, "value must be a string"); + return -1; + } + status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION, + PyString_AS_STRING(value), PyString_GET_SIZE(value), + *attribute, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_SetOCIAttr()") < 0) + return -1; + return 0; +} +#endif + + +//----------------------------------------------------------------------------- +// Connection_Attach() +// Attach to an existing connection. +//----------------------------------------------------------------------------- +static int Connection_Attach( + udt_Connection *self, // connection + OCISvcCtx *handle) // handle of connection to attach to +{ + OCISession *sessionHandle; + OCIServer *serverHandle; + sword status; + + // acquire the server handle + status = OCIAttrGet(handle, OCI_HTYPE_SVCCTX, (dvoid**) &serverHandle, 0, + OCI_ATTR_SERVER, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Attach(): determine server handle") < 0) + return -1; + + // acquire the session handle + status = OCIAttrGet(handle, OCI_HTYPE_SVCCTX, (dvoid**) &sessionHandle, 0, + OCI_ATTR_SESSION, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Attach(): determine session handle") < 0) + return -1; + + // allocate the service context handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid*) &self->handle, OCI_HTYPE_SVCCTX, 0, 0); + if (Environment_CheckForError(self->environment, status, + "Connection_Attach(): allocate service context handle") < 0) + return -1; + + // set attribute for server handle + status = OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, serverHandle, 0, + OCI_ATTR_SERVER, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Attach(): set server handle") < 0) + return -1; + + // set attribute for session handle + status = OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, sessionHandle, 0, + OCI_ATTR_SESSION, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Attach(): set session handle") < 0) + return -1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// Connection_Connect() +// Create a new connection object by connecting to the database. +//----------------------------------------------------------------------------- +static int Connection_Connect( + udt_Connection *self, // connection + const char *dsn, // TNS entry + unsigned dsnLength, // length of TNS entry + ub4 mode, // mode to connect as + int threaded, // threaded? + int twophase) // allow two phase commit? +{ + ub4 credentialType = OCI_CRED_EXT; + sword status; + + // allocate the server handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid**) &self->serverHandle, OCI_HTYPE_SERVER, 0, 0); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): allocate server handle") < 0) + return -1; + + // attach to the server + Py_BEGIN_ALLOW_THREADS + status = OCIServerAttach(self->serverHandle, + self->environment->errorHandle, (text*) dsn, dsnLength, + OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): server attach") < 0) + return -1; + + // allocate the service context handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid**) &self->handle, OCI_HTYPE_SVCCTX, 0, 0); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): allocate service context handle") < 0) + return -1; + + // set attribute for server handle + status = OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, self->serverHandle, 0, + OCI_ATTR_SERVER, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set server handle") < 0) + return -1; + + // set the internal and external names; these are needed for global + // transactions but are limited in terms of the lengths of the strings + if (twophase) { + status = OCIAttrSet(self->serverHandle, OCI_HTYPE_SERVER, + (dvoid*) "cx_Oracle", 0, OCI_ATTR_INTERNAL_NAME, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set internal name") < 0) + return -1; + status = OCIAttrSet(self->serverHandle, OCI_HTYPE_SERVER, + (dvoid*) "cx_Oracle", 0, OCI_ATTR_EXTERNAL_NAME, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set external name") < 0) + return -1; + } + + // allocate the session handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid**) &self->sessionHandle, OCI_HTYPE_SESSION, 0, 0); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): allocate session handle") < 0) + return -1; + + // set user name in session handle + if (self->username && PyString_GET_SIZE(self->username) > 0) { + credentialType = OCI_CRED_RDBMS; + status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION, + (text*) PyString_AS_STRING(self->username), + PyString_GET_SIZE(self->username), OCI_ATTR_USERNAME, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set user name") < 0) + return -1; + } + + // set password in session handle + if (self->password && PyString_GET_SIZE(self->password) > 0) { + credentialType = OCI_CRED_RDBMS; + status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION, + (text*) PyString_AS_STRING(self->password), + PyString_GET_SIZE(self->password), OCI_ATTR_PASSWORD, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set password") < 0) + return -1; + } + + // set the session handle on the service context handle + status = OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, + self->sessionHandle, 0, OCI_ATTR_SESSION, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): set session handle") < 0) + return -1; + + // begin the session + Py_BEGIN_ALLOW_THREADS + status = OCISessionBegin(self->handle, self->environment->errorHandle, + self->sessionHandle, credentialType, mode); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Connect(): begin session") < 0) { + self->sessionHandle = NULL; + return -1; + } + + return 0; +} + + +#include "Cursor.c" +#include "Callback.c" + + +//----------------------------------------------------------------------------- +// Connection_New() +// Create a new connection object and return it. +//----------------------------------------------------------------------------- +static PyObject* Connection_New( + PyTypeObject *type, // type object + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + udt_Connection *self; + + // create the object + self = (udt_Connection*) type->tp_alloc(type, 0); + if (!self) + return NULL; + self->commitMode = OCI_DEFAULT; + self->environment = NULL; + + return (PyObject*) self; +} + + +//----------------------------------------------------------------------------- +// Connection_Init() +// Initialize the connection members. +//----------------------------------------------------------------------------- +static int Connection_Init( + udt_Connection *self, // connection + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + unsigned usernameLength, passwordLength, dsnLength; + const char *username, *password, *dsn; + PyObject *threadedObj, *twophaseObj; + int threaded, twophase; +#ifdef ORACLE_9I + udt_SessionPool *pool; +#endif + OCISvcCtx *handle; + ub4 connectMode; + + // define keyword arguments + static char *keywordList[] = { "user", "password", "dsn", "mode", "handle", +#ifdef ORACLE_9I + "pool", +#endif + "threaded", "twophase", NULL }; + + // parse arguments + handle = NULL; + username = password = dsn = NULL; + threadedObj = twophaseObj = NULL; + usernameLength = passwordLength = dsnLength = 0; + threaded = twophase = 0; + connectMode = OCI_DEFAULT; +#ifdef ORACLE_9I + pool = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#s#iiO!OO", +#else + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#s#iiOO", +#endif + keywordList, &username, &usernameLength, &password, + &passwordLength, &dsn, &dsnLength, &connectMode, &handle, +#ifdef ORACLE_9I + &g_SessionPoolType, &pool, +#endif + &threadedObj, &twophaseObj)) + return -1; + if (threadedObj) { + threaded = PyObject_IsTrue(threadedObj); + if (threaded < 0) + return -1; + } + if (twophaseObj) { + twophase = PyObject_IsTrue(twophaseObj); + if (twophase < 0) + return -1; + } + + // set up the environment + self->environment = Environment_New(threaded); + if (!self->environment) + return -1; + + // perform some parsing, if necessary + if (username) { + if (!password) { + password = strchr(username, '/'); + dsn = strchr(username, '@'); + if (password) { + if (dsn && password > dsn) + password = NULL; + else { + password++; + passwordLength = strlen(password); + usernameLength -= passwordLength + 1; + } + } + if (dsn) { + if (dsn < password) + dsn = NULL; + else { + dsn++; + dsnLength = strlen(dsn); + if (password) + passwordLength -= dsnLength + 1; + else usernameLength -= dsnLength + 1; + } + } + } + } + + // create the string for the username + if (username) { + self->username = PyString_FromStringAndSize(username, usernameLength); + if (!self->username) + return -1; + } + + // create the string for the password + if (password) { + self->password = PyString_FromStringAndSize(password, passwordLength); + if (!self->password) + return -1; + } + + // create the string for the TNS entry + if (dsn) { + self->dsn = PyString_FromStringAndSize(dsn, dsnLength); + if (!self->dsn) + return -1; + } + + // handle the different ways of initializing the connection + if (handle) + return Connection_Attach(self, handle); +#ifdef ORACLE_9I + if (pool) + return Connection_Acquire(self, pool); +#endif + return Connection_Connect(self, dsn, dsnLength, connectMode, threaded, + twophase); +} + + +//----------------------------------------------------------------------------- +// Connection_Free() +// Deallocate the connection, disconnecting from the database if necessary. +//----------------------------------------------------------------------------- +static void Connection_Free( + udt_Connection *self) // connection object +{ +#ifdef ORACLE_9I + if (self->sessionPool) { + Py_BEGIN_ALLOW_THREADS + OCITransRollback(self->handle, self->environment->errorHandle, + OCI_DEFAULT); + OCISessionRelease(self->handle, self->environment->errorHandle, NULL, + 0, OCI_DEFAULT); + Py_END_ALLOW_THREADS + Py_DECREF(self->sessionPool); + } +#endif + if (self->sessionHandle) { + Py_BEGIN_ALLOW_THREADS + OCITransRollback(self->handle, self->environment->errorHandle, + OCI_DEFAULT); + OCISessionEnd(self->handle, self->environment->errorHandle, + self->sessionHandle, OCI_DEFAULT); + Py_END_ALLOW_THREADS + } + if (self->serverHandle) + OCIServerDetach(self->serverHandle, + self->environment->errorHandle, OCI_DEFAULT); + Py_XDECREF(self->environment); + Py_XDECREF(self->username); + Py_XDECREF(self->password); + Py_XDECREF(self->dsn); + Py_XDECREF(self->version); + self->ob_type->tp_free((PyObject*) self); +} + + +//----------------------------------------------------------------------------- +// Connection_Repr() +// Return a string representation of the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_Repr( + udt_Connection *connection) // connection to return the string for +{ + PyObject *module, *name, *result; + + if (GetModuleAndName(connection->ob_type, &module, &name) < 0) + return NULL; + if (connection->username && connection->username != Py_None && + connection->dsn && connection->dsn != Py_None) + result = PyString_FromFormat("<%s.%s to %s@%s>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(connection->username), + PyString_AS_STRING(connection->dsn)); + else if (connection->username && connection->username != Py_None) + result = PyString_FromFormat("<%s.%s to user %s@local>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(connection->username)); + else result = PyString_FromFormat("<%s.%s to externally identified user>", + PyString_AS_STRING(module), PyString_AS_STRING(name)); + Py_DECREF(module); + Py_DECREF(name); + return result; +} + + +#ifdef OCI_NLS_CHARSET_MAXBYTESZ +//----------------------------------------------------------------------------- +// Connection_GetCharacterSetName() +// Retrieve the IANA character set name for the attribute. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetCharacterSetName( + udt_Connection *self, // connection object + ub2 attribute) // attribute to fetch +{ + char charsetName[OCI_NLS_MAXBUFSZ], ianaCharsetName[OCI_NLS_MAXBUFSZ]; + ub2 charsetId; + sword status; + + // get character set id + status = OCIAttrGet(self->environment->handle, OCI_HTYPE_ENV, &charsetId, + NULL, attribute, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_GetCharacterSetName(): get character set id") < 0) + return NULL; + + // get character set name + status = OCINlsCharSetIdToName(self->environment->handle, + (text*) charsetName, OCI_NLS_MAXBUFSZ, charsetId); + if (Environment_CheckForError(self->environment, status, + "Connection_GetNEncoding(): get Oracle character set name") < 0) + return NULL; + + // get IANA character set name + status = OCINlsNameMap(self->environment->handle, + (oratext*) ianaCharsetName, OCI_NLS_MAXBUFSZ, + (oratext*) charsetName, OCI_NLS_CS_ORA_TO_IANA); + if (Environment_CheckForError(self->environment, status, + "Connection_GetEncoding(): translate NLS character set") < 0) + return NULL; + + return PyString_FromString(ianaCharsetName); +} + + +//----------------------------------------------------------------------------- +// Connection_GetEncoding() +// Retrieve the IANA encoding used by the client. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetEncoding( + udt_Connection *self, // connection object + void *arg) // optional argument (ignored) +{ + return Connection_GetCharacterSetName(self, OCI_ATTR_ENV_CHARSET_ID); +} + + +//----------------------------------------------------------------------------- +// Connection_GetNationalEncoding() +// Retrieve the IANA national encoding used by the client. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetNationalEncoding( + udt_Connection *self, // connection object + void *arg) // optional argument (ignored) +{ + return Connection_GetCharacterSetName(self, OCI_ATTR_ENV_NCHARSET_ID); +} +#endif + + +//----------------------------------------------------------------------------- +// Connection_GetVersion() +// Retrieve the version of the database and return it. Note that this +// function also places the result in the associated dictionary so it is only +// calculated once. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetVersion( + udt_Connection *self, // connection object + void *arg) // optional argument (ignored) +{ + udt_Variable *versionVar, *compatVar; + PyObject *results, *temp; + udt_Cursor *cursor; + + // if version has already been determined, no need to determine again + if (self->version) { + Py_INCREF(self->version); + return self->version; + } + + // allocate a cursor to retrieve the version + cursor = (udt_Cursor*) Connection_NewCursor(self, NULL); + if (!cursor) + return NULL; + + // allocate version variable + versionVar = Variable_New(cursor, cursor->arraySize, &vt_String, + vt_String.elementLength); + if (!versionVar) { + Py_DECREF(cursor); + return NULL; + } + + // allocate compatibility variable + compatVar = Variable_New(cursor, cursor->arraySize, &vt_String, + vt_String.elementLength); + if (!compatVar) { + Py_DECREF(versionVar); + Py_DECREF(cursor); + return NULL; + } + + // create the parameters for the function call + temp = Py_BuildValue("(s,[OO])", + "begin dbms_utility.db_version(:ver, :compat); end;", + versionVar, compatVar); + Py_DECREF(versionVar); + Py_DECREF(compatVar); + if (!temp) { + Py_DECREF(cursor); + return NULL; + } + + // execute the cursor + results = Cursor_Execute(cursor, temp, NULL); + if (!results) { + Py_DECREF(temp); + Py_DECREF(cursor); + return NULL; + } + Py_DECREF(results); + + // retrieve value + self->version = Variable_GetValue(versionVar, 0); + Py_DECREF(temp); + Py_DECREF(cursor); + Py_XINCREF(self->version); + return self->version; +} + + +//----------------------------------------------------------------------------- +// Connection_GetMaxBytesPerCharacter() +// Return the maximum number of bytes per character. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetMaxBytesPerCharacter( + udt_Connection *self, // connection object + void *arg) // optional argument (ignored) +{ + return PyInt_FromLong(self->environment->maxBytesPerCharacter); +} + + +//----------------------------------------------------------------------------- +// Connection_Close() +// Close the connection, disconnecting from the database. +//----------------------------------------------------------------------------- +static PyObject *Connection_Close( + udt_Connection *self, // connection to close + PyObject *args) // arguments +{ + sword status; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // perform a rollback + Py_BEGIN_ALLOW_THREADS + status = OCITransRollback(self->handle, self->environment->errorHandle, + OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Close(): rollback") < 0) + return NULL; + + // logoff of the server + if (self->sessionHandle) { + Py_BEGIN_ALLOW_THREADS + status = OCISessionEnd(self->handle, self->environment->errorHandle, + self->sessionHandle, OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Close(): end session") < 0) + return NULL; + OCIHandleFree(self->handle, OCI_HTYPE_SVCCTX); + } + self->handle = NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_Commit() +// Commit the transaction on the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_Commit( + udt_Connection *self, // connection to commit + PyObject *args) // arguments +{ + sword status; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // perform the commit + Py_BEGIN_ALLOW_THREADS + status = OCITransCommit(self->handle, self->environment->errorHandle, + self->commitMode); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Commit()") < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_Begin() +// Begin a new transaction on the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_Begin( + udt_Connection *self, // connection to commit + PyObject *args) // arguments +{ + unsigned transactionIdLength, branchIdLength; + const char *transactionId, *branchId; + OCITrans *transactionHandle; + int formatId; + sword status; + XID xid; + + // parse the arguments + formatId = -1; + transactionIdLength = branchIdLength = 0; + if (!PyArg_ParseTuple(args, "|is#s#", &formatId, &transactionId, + &transactionIdLength, &branchId, &branchIdLength)) + return NULL; + if (transactionIdLength > MAXGTRIDSIZE) { + PyErr_SetString(PyExc_ValueError, "transaction id too large"); + return NULL; + } + if (branchIdLength > MAXBQUALSIZE) { + PyErr_SetString(PyExc_ValueError, "branch id too large"); + return NULL; + } + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // create the transaction handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid**) &transactionHandle, OCI_HTYPE_TRANS, 0, 0); + if (Environment_CheckForError(self->environment, status, + "Connection_Begin(): allocate transaction handle") < 0) + return NULL; + + // set the XID for the transaction, if applicable + if (formatId != -1) { + xid.formatID = formatId; + xid.gtrid_length = transactionIdLength; + xid.bqual_length = branchIdLength; + if (transactionIdLength > 0) + strncpy(xid.data, transactionId, transactionIdLength); + if (branchIdLength > 0) + strncpy(&xid.data[transactionIdLength], branchId, branchIdLength); + OCIAttrSet(transactionHandle, OCI_HTYPE_TRANS, &xid, sizeof(XID), + OCI_ATTR_XID, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Begin(): set XID") < 0) + return NULL; + } + + // associate the transaction with the connection + OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, transactionHandle, 0, + OCI_ATTR_TRANS, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Begin(): associate transaction") < 0) + return NULL; + + // start the transaction + Py_BEGIN_ALLOW_THREADS + status = OCITransStart(self->handle, self->environment->errorHandle, 0, + OCI_TRANS_NEW); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Begin(): start transaction") < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_Prepare() +// Commit the transaction on the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_Prepare( + udt_Connection *self, // connection to commit + PyObject *args) // arguments +{ + sword status; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // perform the prepare + Py_BEGIN_ALLOW_THREADS + status = OCITransPrepare(self->handle, self->environment->errorHandle, + OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Prepare()") < 0) + return NULL; + self->commitMode = OCI_TRANS_TWOPHASE; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_Rollback() +// Rollback the transaction on the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_Rollback( + udt_Connection *self, // connection to rollback + PyObject *args) // arguments +{ + sword status; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // perform the rollback + Py_BEGIN_ALLOW_THREADS + status = OCITransRollback(self->handle, self->environment->errorHandle, + OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Connection_Rollback()") < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_NewCursor() +// Create a new cursor (statement) referencing the connection. +//----------------------------------------------------------------------------- +static PyObject *Connection_NewCursor( + udt_Connection *self, // connection to create cursor on + PyObject *args) // arguments +{ + PyObject *createArgs, *result; + + createArgs = PyTuple_New(1); + if (!createArgs) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(createArgs, 0, (PyObject*) self); + result = PyObject_Call( (PyObject*) &g_CursorType, createArgs, NULL); + Py_DECREF(createArgs); + return result; +} + + +//----------------------------------------------------------------------------- +// Connection_Cancel() +// Execute an OCIBreak() to cause an immediate (asynchronous) abort of any +// currently executing OCI function. +//----------------------------------------------------------------------------- +static PyObject *Connection_Cancel( + udt_Connection *self, // connection to cancel + PyObject *args) // arguments +{ + sword status; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // perform the break + status = OCIBreak(self->handle, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "Connection_Cancel()") < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Connection_RegisterCallback() +// Register a callback for the OCI function. +//----------------------------------------------------------------------------- +static PyObject *Connection_RegisterCallback( + udt_Connection *self, // connection to register callback on + PyObject *args) // arguments +{ + PyObject *callback, *tuple; + int functionCode, when; + sword status; + + // parse the arguments + if (!PyArg_ParseTuple(args, "iiO", &functionCode, &when, &callback)) + return NULL; + + // create a tuple for passing through to the callback handler + tuple = Py_BuildValue("OO", self, callback); + if (!tuple) + return NULL; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // register the callback with the OCI + status = OCIUserCallbackRegister(self->environment->handle, OCI_HTYPE_ENV, + self->environment->errorHandle, (OCIUserCallback) Callback_Handler, + tuple, functionCode, when, NULL); + if (Environment_CheckForError(self->environment, status, + "Connection_RegisterCallback()") < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +//----------------------------------------------------------------------------- +// Connection_UnregisterCallback() +// Unregister a callback for the OCI function, if one has been registered. +// No error is raised if a callback has not been registered. +//----------------------------------------------------------------------------- +static PyObject *Connection_UnregisterCallback( + udt_Connection *self, // connection to unregister callback on + PyObject *args) // arguments +{ + OCIUserCallback callback; + int functionCode, when; + PyObject *tuple; + sword status; + + // parse the arguments + if (!PyArg_ParseTuple(args, "ii", &functionCode, &when)) + return NULL; + + // make sure we are actually connected + if (Connection_IsConnected(self) < 0) + return NULL; + + // find out if a callback has been registered + status = OCIUserCallbackGet(self->environment->handle, OCI_HTYPE_ENV, + self->environment->errorHandle, functionCode, when, &callback, + (dvoid**) &tuple, NULL); + if (Environment_CheckForError(self->environment, status, + "Connection_UnregisterCallback(): get") < 0) + return NULL; + + // if a callback was registered, clear it + if (callback) { + Py_DECREF(tuple); + status = OCIUserCallbackRegister(self->environment->handle, + OCI_HTYPE_ENV, self->environment->errorHandle, NULL, + NULL, functionCode, when, NULL); + if (Environment_CheckForError(self->environment, status, + "Connection_UnregisterCallback(): clear") < 0) + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + diff --git a/Cursor.c b/Cursor.c new file mode 100644 index 0000000..eb9c993 --- /dev/null +++ b/Cursor.c @@ -0,0 +1,2001 @@ +//----------------------------------------------------------------------------- +// 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 *bindVariables; + PyObject *fetchVariables; + 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*); +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 }, + { "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 }, + { 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 = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "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? +{ +#ifdef ORACLE_9I + sword status; +#endif + + if (self->handle) { +#ifdef ORACLE_9I + if (self->isOwned) { +#endif + OCIHandleFree(self->handle, OCI_HTYPE_STMT); +#ifdef ORACLE_9I + } else { + if (self->connection->handle != 0) { + status = OCIStmtRelease(self->handle, + self->environment->errorHandle, NULL, 0, OCI_DEFAULT); + if (raiseException && Environment_CheckForError( + self->environment, status, "Cursor_FreeHandle()") < 0) + return -1; + } + } +#endif + } + 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 = 1; + self->bindArraySize = 1; + self->statementType = -1; + self->outputSize = -1; + self->outputSizeColumn = -1; + +#ifndef ORACLE_9I + // Oracle 8i must always allocate a handle + if (Cursor_AllocateHandle(self) < 0) + return -1; +#endif + + // mark cursor as open + 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; + + connectionRepr = PyObject_Repr((PyObject*) cursor->connection); + if (!connectionRepr) + return NULL; + if (GetModuleAndName(cursor->ob_type, &module, &name) < 0) { + Py_DECREF(connectionRepr); + return NULL; + } + result = PyString_FromFormat("<%s.%s on %s>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(connectionRepr)); + Py_DECREF(connectionRepr); + Py_DECREF(module); + Py_DECREF(name); + return result; +} + + +//----------------------------------------------------------------------------- +// Cursor_Free() +// Deallocate the cursor. +//----------------------------------------------------------------------------- +static void Cursor_Free( + udt_Cursor *self) // cursor object +{ + Cursor_FreeHandle(self, 0); + Py_XDECREF(self->statement); + Py_XDECREF(self->bindVariables); + Py_XDECREF(self->fetchVariables); + Py_XDECREF(self->connection); + self->ob_type->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) + 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 = PyString_FromStringAndSize(bindNames[i], + bindNameLengths[i]); + 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_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; + + Py_BEGIN_ALLOW_THREADS + status = OCIStmtExecute(self->connection->handle, self->handle, + self->environment->errorHandle, numIters, 0, 0, 0, OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Cursor_InternalExecute()") < 0) { + 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 +{ + PyObject *tuple, *var, *type; + int displaySize, index; + ub2 internalSize; + ub4 nameLength; + sb2 precision; + sword status; + char *name; + ub1 nullOk; + sb1 scale; + + // 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; + + // 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; + var = PyList_GET_ITEM(self->fetchVariables, pos - 1); + if (var->ob_type == &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*) var->ob_type; + if (type == (PyObject*) &g_StringVarType) + displaySize = internalSize; + else if (type == (PyObject*) &g_BinaryVarType) + displaySize = internalSize; + else if (type == (PyObject*) &g_FixedCharVarType) + displaySize = internalSize; + 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) { +#ifdef NATIVE_DATETIME + type = (PyObject*) PyDateTimeAPI->DateTimeType; +#else + type = (PyObject*) &g_ExternalDateTimeVarType; +#endif + 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, PyString_FromStringAndSize(name, nameLength)); + 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; + + // 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 no statement has been executed yet, return None + if (!self->fetchVariables) { + Py_INCREF(Py_None); + return Py_None; + } + + // create a list of the required length + numItems = PyList_GET_SIZE(self->fetchVariables); + 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->handle = 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 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->maxLength); + 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 + } else { + *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 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) < 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) < 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; + + // 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; + } + } + } + } + + // ensure that input sizes are reset + self->setInputSizes = 0; + + return 0; +} + + +//----------------------------------------------------------------------------- +// Cursor_CreateTuple() +// Create a tuple consisting of each of the items in the select-list. +//----------------------------------------------------------------------------- +static PyObject *Cursor_CreateTuple( + udt_Cursor *self) // cursor object +{ + PyObject *tuple, *item; + 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); + } + + self->rowNum++; + self->rowCount++; + 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 +{ + 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 + 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 +#ifdef ORACLE_9I + if (Cursor_FreeHandle(self, 1) < 0) + return -1; +#endif + + // prepare statement + Py_BEGIN_ALLOW_THREADS +#ifdef ORACLE_9I + self->isOwned = 0; + status = OCIStmtPrepare2(self->connection->handle, &self->handle, + self->environment->errorHandle, + (text*) PyString_AS_STRING(statement), + PyString_GET_SIZE(statement), NULL, 0, OCI_NTV_SYNTAX, + OCI_DEFAULT); +#else + status = OCIStmtPrepare(self->handle, self->environment->errorHandle, + (text*) PyString_AS_STRING(statement), + PyString_GET_SIZE(statement), OCI_NTV_SYNTAX, OCI_DEFAULT); +#endif + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "Cursor_InternalPrepare(): prepare") < 0) { +#ifdef ORACLE_9I + // 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; +#endif + return -1; + } + + // clear bind variables, if applicable + if (!self->setInputSizes) { + Py_XDECREF(self->bindVariables); + self->bindVariables = NULL; + } + + // determine if statement is a query + if (Cursor_GetStatementType(self) < 0) + return -1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// Cursor_Parse() +// Parse the statement without executing it. +//----------------------------------------------------------------------------- +static PyObject *Cursor_Parse( + udt_Cursor *self, // cursor to perform prepare on + PyObject *args) // arguments +{ + PyObject *statement; + sword status; + + // 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) < 0) + return NULL; + + // parse the statement + Py_BEGIN_ALLOW_THREADS + status = OCIStmtExecute(self->connection->handle, self->handle, + self->environment->errorHandle, 0, 0, 0, 0, OCI_PARSE_ONLY); + 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; + + // 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) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// 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) + const char *name, // name of procedure/function to call + unsigned nameLength, // length of name of procedure/function + PyObject *listOfArguments) // arguments to procedure/function +{ + PyObject *bindVariables, *results, *arguments; + int numArguments, statementSize, i, offset; + char *statement, *ptr; + + // determine the number of arguments passed + if (listOfArguments) { + if (!PySequence_Check(listOfArguments)) { + PyErr_SetString(PyExc_TypeError, "arguments must be a sequence"); + return -1; + } + numArguments = PySequence_Size(listOfArguments); + if (numArguments < 0) + return -1; + } else { + numArguments = 0; + listOfArguments = PyList_New(0); + if (!listOfArguments) + return -1; + } + + // make sure the cursor is open + if (Cursor_IsOpen(self) < 0) + return -1; + + // determine the statement size add the return value, if applicable + offset = 0; + statementSize = nameLength + numArguments * 9 + 15; + if (returnValue) { + offset = 1; + statementSize += 10; + bindVariables = PySequence_List(listOfArguments); + if (!bindVariables) + return -1; + if (PyList_Insert(bindVariables, 0, (PyObject*) returnValue) < 0) { + Py_DECREF(bindVariables); + return -1; + } + } else { + Py_INCREF(listOfArguments); + bindVariables = listOfArguments; + } + + // allocate a string for the statement + statement = (char*) PyMem_Malloc(statementSize); + if (!statement) { + Py_DECREF(bindVariables); + PyErr_NoMemory(); + return -1; + } + + // build up the statement + arguments = PySequence_Fast(listOfArguments, + "expecting sequence of arguments"); + if (!arguments) { + Py_DECREF(bindVariables); + PyMem_Free(statement); + return -1; + } + strcpy(statement, "begin "); + if (returnValue) + strcat(statement, ":1 := "); + strcat(statement, name); + ptr = statement + strlen(statement); + *ptr++ = '('; + for (i = 0; i < numArguments; i++) { + if (i > 0) + *ptr++ = ','; + ptr += sprintf(ptr, ":%d", i + offset + 1); +#if (PY_VERSION_HEX >= 0x02030000) + if (PyBool_Check(PySequence_Fast_GET_ITEM(arguments, i))) + ptr += sprintf(ptr, " = 1"); +#endif + } + strcpy(ptr, "); end;"); + Py_DECREF(arguments); + + // execute the statement on the cursor + results = PyObject_CallMethod( (PyObject*) self, "execute", "sO", + statement, bindVariables); + PyMem_Free(statement); + 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", NULL }; + PyObject *listOfArguments, *returnType, *results; + udt_Variable *var; + int nameLength; + char *name; + + // expect stored function name, return type and optionally a list of + // arguments + listOfArguments = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "s#O|O", keywordList, + &name, &nameLength, &returnType, &listOfArguments)) + 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, nameLength, listOfArguments) < 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", NULL }; + PyObject *listOfArguments, *results, *var, *temp; + int nameLength, numArgs, i; + char *name; + + // expect stored procedure name and optionally a list of arguments + listOfArguments = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "s#|O", keywordList, + &name, &nameLength, &listOfArguments)) + return NULL; + + // call the stored procedure + if (Cursor_Call(self, NULL, name, nameLength, listOfArguments) < 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 (statement != Py_None && !PyString_Check(statement)) { + PyErr_SetString(PyExc_TypeError, "expecting None or a string"); + 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) < 0) + return NULL; + + // perform binds + if (executeArgs && Cursor_SetBindVariables(self, executeArgs, 1, 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 defined variables for possible use + if (isQuery) { + Py_INCREF(self->fetchVariables); + return self->fetchVariables; + } + + // 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; + if (statement != Py_None && !PyString_Check(statement)) { + PyErr_SetString(PyExc_TypeError, "expecting None or a string"); + return NULL; + } + + // make sure the cursor is open + if (Cursor_IsOpen(self) < 0) + return NULL; + + // prepare the statement + if (Cursor_InternalPrepare(self, statement) < 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) < 0) + return NULL; + } + if (Cursor_PerformBind(self) < 0) + return NULL; + + // execute the statement + if (Cursor_InternalExecute(self, PyList_GET_SIZE(listOfArguments)) < 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; + } + 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; + } + for (i = 0; i < PyList_GET_SIZE(self->fetchVariables); i++) { + var = (udt_Variable*) PyList_GET_ITEM(self->fetchVariables, i); + var->internalFetchNum++; + } + 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, *tuple; + int row, status; + + // create an empty list + results = PyList_New(0); + if (!results) + return NULL; + + // fetch as many rows as possible + for (row = 0; rowLimit == 0 || row < rowLimit; row++) { + status = Cursor_MoreRows(self); + if (status < 0) { + Py_DECREF(results); + return NULL; + } else if (status == 0) { + break; + } else { + tuple = Cursor_CreateTuple(self); + if (!tuple) { + Py_DECREF(results); + return NULL; + } + if (PyList_Append(results, tuple) < 0) { + Py_DECREF(tuple); + Py_DECREF(results); + return NULL; + } + Py_DECREF(tuple); + } + } + + 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_CreateTuple(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 +{ + udt_VariableType *varType; + PyObject *type; + int length; + + // parse arguments + length = 0; + if (!PyArg_ParseTuple(args, "O|i", &type, &length)) + return NULL; + + // determine the type of variable + varType = Variable_TypeByPythonType(self, type); + if (!varType) + return NULL; + if (varType->isVariableLength && length == 0) + length = varType->elementLength; + + return (PyObject*) Variable_New(self, self->bindArraySize, varType, + length); +} + + +//----------------------------------------------------------------------------- +// 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; + int length, numElements; + PyObject *type, *value; + udt_Variable *var; + + // parse arguments + length = 0; + if (!PyArg_ParseTuple(args, "OO|i", &type, &value, &length)) + return NULL; + + // determine the type of variable + varType = Variable_TypeByPythonType(self, type); + if (!varType) + return NULL; + if (varType->isVariableLength && length == 0) + length = varType->elementLength; + + // determine the number of elements to create + if (PyList_Check(value)) + numElements = PyList_GET_SIZE(value); + else if (PyInt_Check(value)) + numElements = PyInt_AS_LONG(value); + else { + PyErr_SetString(PyExc_TypeError, + "expecting integer or list of values"); + return NULL; + } + + // create the variable + var = Variable_New(self, numElements, varType, length); + 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_CreateTuple(self); + + // no more rows, return NULL without setting an exception + return NULL; +} + diff --git a/CursorVar.c b/CursorVar.c new file mode 100644 index 0000000..c6df41c --- /dev/null +++ b/CursorVar.c @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// CursorVar.c +// Defines the routines specific to the cursor type. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Cursor variable type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + OCIStmt **data; + udt_Connection *connection; + PyObject *cursors; +} udt_CursorVar; + + +//----------------------------------------------------------------------------- +// Declaration of cursor variable functions. +//----------------------------------------------------------------------------- +static int CursorVar_Initialize(udt_CursorVar*, udt_Cursor*); +static void CursorVar_Finalize(udt_CursorVar*); +static int CursorVar_SetValue(udt_CursorVar*, unsigned, PyObject*); +static PyObject *CursorVar_GetValue(udt_CursorVar*, unsigned); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_CursorVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.CURSOR", // tp_name + sizeof(udt_CursorVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_Cursor = { + (InitializeProc) CursorVar_Initialize, + (FinalizeProc) CursorVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) CursorVar_SetValue, + (GetValueProc) CursorVar_GetValue, + &g_CursorVarType, // Python type + SQLT_RSET, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCIStmt*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +//----------------------------------------------------------------------------- +// CursorVar_Initialize() +// Initialize the variable. +//----------------------------------------------------------------------------- +static int CursorVar_Initialize( + udt_CursorVar *var, // variable to initialize + udt_Cursor *cursor) // cursor created by +{ + udt_Cursor *tempCursor; + ub4 i; + + Py_INCREF(cursor->connection); + var->connection = cursor->connection; + var->cursors = PyList_New(var->allocatedElements); + if (!var->cursors) + return -1; + for (i = 0; i < var->allocatedElements; i++) { + tempCursor = (udt_Cursor*) Connection_NewCursor(var->connection, NULL); + if (!tempCursor) { + Py_DECREF(var); + return -1; + } + PyList_SET_ITEM(var->cursors, i, (PyObject*) tempCursor); + if (Cursor_AllocateHandle(tempCursor) < 0) { + Py_DECREF(var); + return -1; + } + var->data[i] = tempCursor->handle; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// CursorVar_Finalize() +// Prepare for variable destruction. +//----------------------------------------------------------------------------- +static void CursorVar_Finalize( + udt_CursorVar *var) // variable to free +{ + Py_DECREF(var->connection); + Py_XDECREF(var->cursors); +} + + +//----------------------------------------------------------------------------- +// CursorVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int CursorVar_SetValue( + udt_CursorVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + udt_Cursor *cursor; + + if (!PyObject_IsInstance(value, (PyObject*) &g_CursorType)) { + PyErr_SetString(PyExc_TypeError, "expecting cursor"); + return -1; + } + + Py_XDECREF(PyList_GET_ITEM(var->cursors, pos)); + Py_INCREF(value); + PyList_SET_ITEM(var->cursors, pos, value); + cursor = (udt_Cursor *) value; +#ifdef ORACLE_9I + if (!cursor->isOwned) { + if (Cursor_FreeHandle(cursor, 1) < 0) + return -1; + cursor->isOwned = 1; + if (Cursor_AllocateHandle(cursor) < 0) + return -1; + } +#endif + var->data[pos] = cursor->handle; + cursor->statementType = -1; + return 0; +} + + +//----------------------------------------------------------------------------- +// CursorVar_GetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static PyObject *CursorVar_GetValue( + udt_CursorVar *var, // variable to set value for + unsigned pos) // array position to set +{ + PyObject *cursor; + + cursor = PyList_GET_ITEM(var->cursors, pos); + ((udt_Cursor*) cursor)->statementType = -1; + Py_INCREF(cursor); + return cursor; +} + diff --git a/DateTimeVar.c b/DateTimeVar.c new file mode 100644 index 0000000..58bbc8f --- /dev/null +++ b/DateTimeVar.c @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// DateTimeVar.c +// Defines the routines for handling date (time) variables. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// DateTime type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + OCIDate *data; +} udt_DateTimeVar; + + +//----------------------------------------------------------------------------- +// Declaration of date/time variable functions. +//----------------------------------------------------------------------------- +static int DateTimeVar_SetValue(udt_DateTimeVar*, unsigned, PyObject*); +static PyObject *DateTimeVar_GetValue(udt_DateTimeVar*, unsigned); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_DateTimeVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.DATETIME", // tp_name + sizeof(udt_DateTimeVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_DateTime = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) DateTimeVar_SetValue, + (GetValueProc) DateTimeVar_GetValue, + &g_DateTimeVarType, // Python type + SQLT_ODT, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCIDate), // element length (default) + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +#ifdef NATIVE_DATETIME +static udt_VariableType vt_Date = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) DateTimeVar_SetValue, + (GetValueProc) DateTimeVar_GetValue, + &g_DateTimeVarType, // Python type + SQLT_ODT, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCIDate), // element length (default) + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +//----------------------------------------------------------------------------- +// DateTimeVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int DateTimeVar_SetValue( + udt_DateTimeVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + ub1 month, day, hour, minute, second; + short year; + + if (PyDateTime_Check(value)) { + year = (short) PyDateTime_GET_YEAR(value); + month = PyDateTime_GET_MONTH(value); + day = PyDateTime_GET_DAY(value); + hour = PyDateTime_DATE_GET_HOUR(value); + minute = PyDateTime_DATE_GET_MINUTE(value); + second = PyDateTime_DATE_GET_SECOND(value); + } else if (PyDate_Check(value)) { + year = (short) PyDateTime_GET_YEAR(value); + month = PyDateTime_GET_MONTH(value); + day = PyDateTime_GET_DAY(value); + hour = minute = second = 0; + } else { + PyErr_SetString(PyExc_TypeError, "expecting date data"); + return -1; + } + + // store a copy of the value + OCIDateSetDate(&var->data[pos], year, month, day); + OCIDateSetTime(&var->data[pos], hour, minute, second); + + return 0; +} + +#else + +//----------------------------------------------------------------------------- +// DateTimeVar_GetAttribute() +// Get the attribute from the object and convert it to an integer. +//----------------------------------------------------------------------------- +static int DateTimeVar_GetAttribute( + PyObject *value, // value to get attribute for + char *name, // name to acquire + unsigned *outputValue) // output value +{ + PyObject *attrValue; + + attrValue = PyObject_GetAttrString(value, name); + if (!attrValue) + return -1; + *outputValue = PyInt_AsLong(attrValue); + if (PyErr_Occurred()) + return -1; + + return 0; +} + +//----------------------------------------------------------------------------- +// DateTimeVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int DateTimeVar_SetValue( + udt_DateTimeVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + unsigned year, month, day, hour, minute, second; + udt_ExternalDateTimeVar *dateValue; + sword status; + uword valid; + + // handle internal cx_Oracle date type + if (value->ob_type == &g_ExternalDateTimeVarType) { + dateValue = (udt_ExternalDateTimeVar*) value; + year = dateValue->year; + month = dateValue->month; + day = dateValue->day; + hour = dateValue->hour; + minute = dateValue->minute; + second = dateValue->second; + + // handle Python 2.3 datetime type + } else if (value->ob_type == g_DateTimeType) { + if (DateTimeVar_GetAttribute(value, "year", &year) < 0) + return -1; + if (DateTimeVar_GetAttribute(value, "month", &month) < 0) + return -1; + if (DateTimeVar_GetAttribute(value, "day", &day) < 0) + return -1; + if (DateTimeVar_GetAttribute(value, "hour", &hour) < 0) + return -1; + if (DateTimeVar_GetAttribute(value, "minute", &minute) < 0) + return -1; + if (DateTimeVar_GetAttribute(value, "second", &second) < 0) + return -1; + } else { + PyErr_SetString(PyExc_TypeError, "expecting date data"); + return -1; + } + + // store a copy of the value + OCIDateSetDate(&var->data[pos], (sb2) year, (ub1) month, (ub1) day); + OCIDateSetTime(&var->data[pos], (ub1) hour, (ub1) minute, (ub1) second); + status = OCIDateCheck(var->environment->errorHandle, &var->data[pos], + &valid); + if (Environment_CheckForError(var->environment, status, + "DateTimeVar_SetValue()") < 0) + return -1; + if (valid != 0) { + PyErr_Format(g_DataErrorException, "invalid date: %d/%d/%d %d:%d:%d", + year, month, day, hour, minute, second); + return -1; + } + + return 0; +} +#endif + + +//----------------------------------------------------------------------------- +// DateTimeVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *DateTimeVar_GetValue( + udt_DateTimeVar *var, // variable to determine value for + unsigned pos) // array position +{ + return OracleDateToPythonDate(var->type, &var->data[pos]); +} + diff --git a/Environment.c b/Environment.c new file mode 100644 index 0000000..0e9d931 --- /dev/null +++ b/Environment.c @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Environment.c +// Environment handling. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// structure for the Python type +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + OCIEnv *handle; + OCIError *errorHandle; + int maxBytesPerCharacter; + int fixedWidth; + ub4 maxStringBytes; +} udt_Environment; + +//----------------------------------------------------------------------------- +// maximum number of characters applicable to strings +//----------------------------------------------------------------------------- +#define MAX_STRING_CHARS 4000 + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +static void Environment_Free(udt_Environment*); +static int Environment_CheckForError(udt_Environment*, sword, const char*); + +//----------------------------------------------------------------------------- +// declaration of Python type +//----------------------------------------------------------------------------- +static PyTypeObject g_EnvironmentType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "OracleEnvironment", // tp_name + sizeof(udt_Environment), // tp_basicsize + 0, // tp_itemsize + (destructor) Environment_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // 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, // tp_flags + 0 // tp_doc +}; + + +#include "Error.c" + + +//----------------------------------------------------------------------------- +// Environment_New() +// Create a new environment object. +//----------------------------------------------------------------------------- +static udt_Environment *Environment_New( + int threaded) // use threaded mode? +{ + udt_Environment *environment; + sword status; + ub4 mode; + + // create a new object for the Oracle environment + environment = PyObject_NEW(udt_Environment, &g_EnvironmentType); + if (!environment) + return NULL; + environment->handle = NULL; + environment->errorHandle = NULL; + environment->maxBytesPerCharacter = 1; + environment->fixedWidth = 1; + environment->maxStringBytes = MAX_STRING_CHARS; + + // turn threading mode on, if desired + mode = OCI_OBJECT; + if (threaded) + mode |= OCI_THREADED; + + // create the environment handle + status = OCIEnvCreate(&environment->handle, mode, NULL, NULL, + NULL, NULL, 0, NULL); + if (!environment->handle) { + Py_DECREF(environment); + PyErr_SetString(PyExc_RuntimeError, + "Unable to acquire Oracle environment handle"); + return NULL; + } + if (Environment_CheckForError(environment, status, + "Environment_New(): create env handle") < 0) { + environment->handle = NULL; + Py_DECREF(environment); + return NULL; + } + + // create the error handle + status = OCIHandleAlloc(environment->handle, + (dvoid**) &environment->errorHandle, OCI_HTYPE_ERROR, 0, 0); + if (Environment_CheckForError(environment, status, + "Environment_New(): create error handle") < 0) { + Py_DECREF(environment); + return NULL; + } + + // acquire max bytes per character +#ifdef OCI_NLS_CHARSET_MAXBYTESZ + status = OCINlsNumericInfoGet(environment->handle, + environment->errorHandle, &environment->maxBytesPerCharacter, + OCI_NLS_CHARSET_MAXBYTESZ); + if (Environment_CheckForError(environment, status, + "Environment_New(): get max bytes per character") < 0) { + Py_DECREF(environment); + return NULL; + } + environment->maxStringBytes = + MAX_STRING_CHARS * environment->maxBytesPerCharacter; + + // acquire whether character set is fixed width + status = OCINlsNumericInfoGet(environment->handle, + environment->errorHandle, &environment->fixedWidth, + OCI_NLS_CHARSET_FIXEDWIDTH); + if (Environment_CheckForError(environment, status, + "Environment_New(): determine if charset is fixed width") < 0) { + Py_DECREF(environment); + return NULL; + } +#endif + + return environment; +} + + +//----------------------------------------------------------------------------- +// Environment_Free() +// Deallocate the environment. Note that destroying the environment handle +// will automatically destroy any child handles that were created. +//----------------------------------------------------------------------------- +static void Environment_Free( + udt_Environment *environment) // environment object +{ + if (environment->handle) + OCIHandleFree(environment->handle, OCI_HTYPE_ENV); + PyObject_DEL(environment); +} + + +//----------------------------------------------------------------------------- +// Environment_RaiseError() +// Reads the error that was caused by the last Oracle statement and raise an +// exception for Python. At this point it is assumed that the Oracle +// environment is fully initialized. +//----------------------------------------------------------------------------- +static void Environment_RaiseError( + udt_Environment *environment, // environment to raise error for + const char *context) // context in which error occurred +{ + PyObject *exceptionType; + udt_Error *error; + + error = Error_New(environment, context); + if (error) { + if (error->errorNumber == 1 || + (error->errorNumber >= 2290 && error->errorNumber <= 2292)) + exceptionType = g_IntegrityErrorException; + else exceptionType = g_DatabaseErrorException; + PyErr_SetObject(exceptionType, (PyObject*) error); + Py_DECREF(error); + } +} + + +//----------------------------------------------------------------------------- +// Environment_CheckForError() +// Check for an error in the last call and if an error has occurred, raise a +// Python exception. +//----------------------------------------------------------------------------- +static int Environment_CheckForError( + udt_Environment *environment, // environment to raise error in + sword status, // status of last call + const char *context) // context +{ + if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) { + if (status == OCI_INVALID_HANDLE) + PyErr_SetString(g_DatabaseErrorException, "Invalid handle!"); + else Environment_RaiseError(environment, context); + return -1; + } + return 0; +} + diff --git a/Error.c b/Error.c new file mode 100644 index 0000000..17768a1 --- /dev/null +++ b/Error.c @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Error.c +// Error handling. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// structure for the Python type +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + sb4 errorNumber; + char errorText[1024]; + const char *context; +} udt_Error; + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +static void Error_Free(udt_Error*); +static PyObject *Error_Str(udt_Error*); + + +//----------------------------------------------------------------------------- +// declaration of members +//----------------------------------------------------------------------------- +static PyMemberDef g_ErrorMembers[] = { + { "code", T_INT, offsetof(udt_Error, errorNumber), READONLY }, + { "message", T_STRING_INPLACE, offsetof(udt_Error, errorText), READONLY }, + { "context", T_STRING, offsetof(udt_Error, context), READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of Python type +//----------------------------------------------------------------------------- +static PyTypeObject g_ErrorType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle._Error", // tp_name + sizeof(udt_Error), // tp_basicsize + 0, // tp_itemsize + (destructor) Error_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + (reprfunc) Error_Str, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + g_ErrorMembers, // tp_members + 0 // tp_getset +}; + + +//----------------------------------------------------------------------------- +// Error_New() +// Create a new error object. +//----------------------------------------------------------------------------- +static udt_Error *Error_New( + udt_Environment *environment, // environment object + const char *context) // context in which error occurred +{ + udt_Error *error; + ub4 handleType; + dvoid *handle; + sword status; + + error = PyObject_NEW(udt_Error, &g_ErrorType); + if (!error) + return NULL; + error->context = context; + if (environment->errorHandle) { + handle = environment->errorHandle; + handleType = OCI_HTYPE_ERROR; + } else { + handle = environment->handle; + handleType = OCI_HTYPE_ENV; + } + status = OCIErrorGet(handle, 1, 0, &error->errorNumber, + (unsigned char*) error->errorText, sizeof(error->errorText), + handleType); + if (status != OCI_SUCCESS) { + Py_DECREF(error); + PyErr_SetString(g_InternalErrorException, "No Oracle error?"); + return NULL; + } + return error; +} + + +//----------------------------------------------------------------------------- +// Error_Free() +// Deallocate the environment, disconnecting from the database if necessary. +//----------------------------------------------------------------------------- +static void Error_Free( + udt_Error *self) // error object +{ + self->ob_type->tp_free((PyObject*) self); +} + + +//----------------------------------------------------------------------------- +// Error_Str() +// Return a string representation of the error variable. +//----------------------------------------------------------------------------- +static PyObject *Error_Str( + udt_Error *self) // variable to return the string for +{ + return PyString_FromString(self->errorText); +} + diff --git a/ExternalDateTimeVar.c b/ExternalDateTimeVar.c new file mode 100644 index 0000000..5188c07 --- /dev/null +++ b/ExternalDateTimeVar.c @@ -0,0 +1,239 @@ +//----------------------------------------------------------------------------- +// ExternalDateTimeVar.c +// Defines the routines for handling date variables external to this module. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// external date/time type +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + unsigned year; + unsigned month; + unsigned day; + unsigned hour; + unsigned minute; + unsigned second; + unsigned fsecond; +} udt_ExternalDateTimeVar; + + +//----------------------------------------------------------------------------- +// Declaration of external date variable functions. +//----------------------------------------------------------------------------- +static void ExternalDateTimeVar_Free(udt_ExternalDateTimeVar*); +static PyObject *ExternalDateTimeVar_Str(udt_ExternalDateTimeVar*); +static int ExternalDateTimeVar_Cmp(udt_ExternalDateTimeVar*, + udt_ExternalDateTimeVar*); +static PyObject *ExternalDateTimeVar_New(PyTypeObject*, PyObject*, PyObject*); +static PyObject *ExternalDateTimeVar_Reduce(udt_ExternalDateTimeVar*); + + +//----------------------------------------------------------------------------- +// declaration of members for Python type +//----------------------------------------------------------------------------- +static PyMemberDef g_ExternalDateTimeVarMembers[] = { + { "year", T_INT, offsetof(udt_ExternalDateTimeVar, year), READONLY }, + { "month", T_INT, offsetof(udt_ExternalDateTimeVar, month), READONLY }, + { "day", T_INT, offsetof(udt_ExternalDateTimeVar, day), READONLY }, + { "hour", T_INT, offsetof(udt_ExternalDateTimeVar, hour), READONLY }, + { "minute", T_INT, offsetof(udt_ExternalDateTimeVar, minute), READONLY }, + { "second", T_INT, offsetof(udt_ExternalDateTimeVar, second), READONLY }, + { "fsecond", T_INT, offsetof(udt_ExternalDateTimeVar, fsecond), READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of methods for Python type +//----------------------------------------------------------------------------- +static PyMethodDef g_ExternalDateTimeVarMethods[] = { + {"__reduce__", (PyCFunction) ExternalDateTimeVar_Reduce, METH_NOARGS }, + { NULL, NULL } +}; + + +//----------------------------------------------------------------------------- +// Python type declaration +//----------------------------------------------------------------------------- +static PyTypeObject g_ExternalDateTimeVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.Timestamp", // tp_name + sizeof(udt_ExternalDateTimeVar), // tp_basicsize + 0, // tp_itemsize + (destructor) ExternalDateTimeVar_Free, + // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + (cmpfunc) ExternalDateTimeVar_Cmp, // tp_compare + (reprfunc) ExternalDateTimeVar_Str, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + (reprfunc) ExternalDateTimeVar_Str, // tp_str + // tp_getattro + 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 + 0, // tp_iter + 0, // tp_iternext + g_ExternalDateTimeVarMethods, // tp_methods + g_ExternalDateTimeVarMembers, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + ExternalDateTimeVar_New, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0 // tp_bases +}; + + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_NewFromC() +// Create a new external date variable from C code. +//----------------------------------------------------------------------------- +PyObject *ExternalDateTimeVar_NewFromC( + PyTypeObject *type, // type of object + unsigned year, // year + unsigned month, // month + unsigned day, // day + unsigned hour, // hour + unsigned minute, // minute + unsigned second, // second + unsigned fsecond) // fractional seconds +{ + udt_ExternalDateTimeVar *var; + + var = (udt_ExternalDateTimeVar*) type->tp_alloc(type, 0); + if (var) { + var->year = year; + var->month = month; + var->day = day; + var->hour = hour; + var->minute = minute; + var->second = second; + var->fsecond = fsecond; + } + + return (PyObject*) var; +} + + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_New() +// Create a new cursor object. +//----------------------------------------------------------------------------- +static PyObject *ExternalDateTimeVar_New( + PyTypeObject *type, // type object + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + unsigned year, month, day, hour, minute, second, fsecond; + + hour = minute = second = fsecond = 0; + if (!PyArg_ParseTuple(args, "iii|iiii", &year, &month, &day, &hour, + &minute, &second, &fsecond)) + return NULL; + return ExternalDateTimeVar_NewFromC(type, year, month, day, hour, minute, + second, fsecond); +} + + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_Reduce() +// Provide information for pickling and unpickling. +//----------------------------------------------------------------------------- +static PyObject* ExternalDateTimeVar_Reduce( + udt_ExternalDateTimeVar* self) // object to pickle +{ + return Py_BuildValue("(O, (iiiiiii))", &g_ExternalDateTimeVarType, + self->year, self->month, self->day, self->hour, self->minute, + self->second, self->fsecond); +} + + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_Free() +// Free an external date variable. +//----------------------------------------------------------------------------- +static void ExternalDateTimeVar_Free( + udt_ExternalDateTimeVar *var) // variable to free +{ + PyObject_DEL(var); +} + + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_Str() +// Return the string representation of the external date variable object. +//----------------------------------------------------------------------------- +static PyObject *ExternalDateTimeVar_Str( + udt_ExternalDateTimeVar *var) // external date variable object +{ + char value[100]; + + sprintf(value, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", var->year, + var->month, var->day, var->hour, var->minute, var->second); + if (var->fsecond > 0) + sprintf(value + strlen(value), ".%.6d", var->fsecond); + return PyString_FromString(value); +} + +//----------------------------------------------------------------------------- +// ExternalDateTimeVar_Cmp() +// Return -1 if the second date is less than the first; 0 if the dates are +// the same and +1 if the second date is greater than the first. +//----------------------------------------------------------------------------- +static int ExternalDateTimeVar_Cmp( + udt_ExternalDateTimeVar *var1, // first date + udt_ExternalDateTimeVar *var2) // second date +{ + if (var1->year < var2->year) + return -1; + if (var1->year > var2->year) + return 1; + if (var1->month < var2->month) + return -1; + if (var1->month > var2->month) + return 1; + if (var1->day < var2->day) + return -1; + if (var1->day > var2->day) + return 1; + if (var1->hour < var2->hour) + return -1; + if (var1->hour > var2->hour) + return 1; + if (var1->minute < var2->minute) + return -1; + if (var1->minute > var2->minute) + return 1; + if (var1->second < var2->second) + return -1; + if (var1->second > var2->second) + return 1; + if (var1->fsecond < var2->fsecond) + return -1; + if (var1->fsecond > var2->fsecond) + return 1; + + return 0; +} + diff --git a/ExternalLobVar.c b/ExternalLobVar.c new file mode 100644 index 0000000..ee7404b --- /dev/null +++ b/ExternalLobVar.c @@ -0,0 +1,518 @@ +//----------------------------------------------------------------------------- +// ExternalLobVar.c +// Defines the routines for handling LOB variables external to this module. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// external LOB type +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + udt_LobVar *lobVar; + unsigned pos; + unsigned internalFetchNum; +} udt_ExternalLobVar; + + +//----------------------------------------------------------------------------- +// Declaration of external LOB variable functions. +//----------------------------------------------------------------------------- +static void ExternalLobVar_Free(udt_ExternalLobVar*); +static PyObject *ExternalLobVar_Str(udt_ExternalLobVar*); +static PyObject *ExternalLobVar_GetAttr(udt_ExternalLobVar*, PyObject*); +static PyObject *ExternalLobVar_Size(udt_ExternalLobVar*, PyObject*); +static PyObject *ExternalLobVar_Read(udt_ExternalLobVar*, PyObject*, + PyObject*); +static PyObject *ExternalLobVar_Write(udt_ExternalLobVar*, PyObject*, + PyObject*); +static PyObject *ExternalLobVar_Trim(udt_ExternalLobVar*, PyObject*, + PyObject*); +static PyObject *ExternalLobVar_GetFileName(udt_ExternalLobVar*, PyObject*); +static PyObject *ExternalLobVar_SetFileName(udt_ExternalLobVar*, PyObject*); +static PyObject *ExternalLobVar_FileExists(udt_ExternalLobVar*, PyObject*); +static PyObject *ExternalLobVar_Reduce(udt_ExternalLobVar*); + + +//----------------------------------------------------------------------------- +// Python type declaration +//----------------------------------------------------------------------------- +static PyTypeObject g_ExternalLobVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.LOB", // tp_name + sizeof(udt_ExternalLobVar), // tp_basicsize + 0, // tp_itemsize + (destructor) ExternalLobVar_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + (reprfunc) ExternalLobVar_Str, // tp_str + (getattrofunc) ExternalLobVar_GetAttr, + // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// declaration of methods for Python type "ExternalLOBVar" +//----------------------------------------------------------------------------- +static PyMethodDef g_ExternalLobVarMethods[] = { + { "size", (PyCFunction) ExternalLobVar_Size, METH_NOARGS }, + { "read", (PyCFunction) ExternalLobVar_Read, + METH_VARARGS | METH_KEYWORDS }, + { "write", (PyCFunction) ExternalLobVar_Write, + METH_VARARGS | METH_KEYWORDS }, + { "trim", (PyCFunction) ExternalLobVar_Trim, + METH_VARARGS | METH_KEYWORDS }, + { "getfilename", (PyCFunction) ExternalLobVar_GetFileName, METH_NOARGS }, + { "setfilename", (PyCFunction) ExternalLobVar_SetFileName, METH_VARARGS }, + { "fileexists", (PyCFunction) ExternalLobVar_FileExists, METH_NOARGS }, + { "__reduce__", (PyCFunction) ExternalLobVar_Reduce, METH_NOARGS }, + { NULL, NULL } +}; + + +//----------------------------------------------------------------------------- +// ExternalLobVar_New() +// Create a new external LOB variable. +//----------------------------------------------------------------------------- +PyObject *ExternalLobVar_New( + udt_LobVar *var, // variable to encapsulate + unsigned pos) // position in array to encapsulate +{ + udt_ExternalLobVar *newVar; + + newVar = PyObject_NEW(udt_ExternalLobVar, &g_ExternalLobVarType); + if (!newVar) + return NULL; + newVar->pos = pos; + newVar->internalFetchNum = var->internalFetchNum; + Py_INCREF(var); + newVar->lobVar = var; + + return (PyObject*) newVar; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Free() +// Free an external LOB variable. +//----------------------------------------------------------------------------- +static void ExternalLobVar_Free( + udt_ExternalLobVar *var) // variable to free +{ + Py_DECREF(var->lobVar); + PyObject_DEL(var); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Verify() +// Verify that the external LOB var is still valid. +//----------------------------------------------------------------------------- +static int ExternalLobVar_Verify( + udt_ExternalLobVar *var) // variable to verify +{ + if (var->internalFetchNum != var->lobVar->internalFetchNum) { + PyErr_SetString(g_ProgrammingErrorException, + "LOB variable no longer valid after subsequent fetch"); + return -1; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_GetAttr() +// Retrieve an attribute on the external LOB variable object. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_GetAttr( + udt_ExternalLobVar *var, // cursor object + PyObject *name) // name of attribute +{ + return Py_FindMethod(g_ExternalLobVarMethods, (PyObject*) var, + PyString_AS_STRING(name)); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_InternalRead() +// Return the size of the LOB variable for internal comsumption. +//----------------------------------------------------------------------------- +static int ExternalLobVar_InternalRead( + udt_ExternalLobVar *var, // variable to return the size of + char *buffer, // buffer in which to put data + ub4 bufferSize, // size of buffer + ub4 *length, // length of data (IN/OUT) + int offset) // offset +{ + sword status; + + if (var->lobVar->isFile) { + status = OCILobFileOpen(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, + var->lobVar->data[var->pos], OCI_FILE_READONLY); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_FileOpen()") < 0) + return -1; + } + + status = OCILobRead(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, + var->lobVar->data[var->pos], length, offset, buffer, + bufferSize, NULL, NULL, 0, var->lobVar->type->charsetForm); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_LobRead()") < 0) { + OCILobFileClose(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, + var->lobVar->data[var->pos]); + return -1; + } + + if (var->lobVar->isFile) { + status = OCILobFileClose(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, + var->lobVar->data[var->pos]); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_FileClose()") < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_InternalSize() +// Return the size of the LOB variable for internal comsumption. +//----------------------------------------------------------------------------- +static int ExternalLobVar_InternalSize( + udt_ExternalLobVar *var) // variable to return the size of +{ + sword status; + ub4 length; + + status = OCILobGetLength(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, + var->lobVar->data[var->pos], &length); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_InternalSize()") < 0) + return -1; + + return length; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Value() +// Return a portion (or all) of the data in the external LOB variable. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Value( + udt_ExternalLobVar *var, // variable to return the size of + int offset, // offset into LOB + int amount) // amount to read from LOB +{ + ub4 length, bufferSize; + PyObject *result; + char *buffer; + + // modify the arguments + if (offset < 0) + offset = 1; + if (amount < 0) { + amount = ExternalLobVar_InternalSize(var); + if (amount < 0) + return NULL; + amount = amount - offset + 1; + if (amount <= 0) + amount = 1; + } + length = amount; + bufferSize = amount * var->lobVar->environment->maxBytesPerCharacter; + + // create a string for retrieving the value + buffer = (char*) PyMem_Malloc(bufferSize); + if (!buffer) + return PyErr_NoMemory(); + if (ExternalLobVar_InternalRead(var, buffer, bufferSize, &length, + offset) < 0) { + PyMem_Free(buffer); + return NULL; + } + + // return the result + if (var->lobVar->environment->fixedWidth) + length = length * var->lobVar->environment->maxBytesPerCharacter; + result = PyString_FromStringAndSize(buffer, length); + PyMem_Free(buffer); + return result; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Size() +// Return the size of the data in the LOB variable. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Size( + udt_ExternalLobVar *var, // variable to return the size of + PyObject *args) // arguments +{ + int length; + + if (ExternalLobVar_Verify(var) < 0) + return NULL; + length = ExternalLobVar_InternalSize(var); + if (length < 0) + return NULL; + return PyInt_FromLong(length); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Read() +// Return a portion (or all) of the data in the external LOB variable. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Read( + udt_ExternalLobVar *var, // variable to return the size of + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + static char *keywordList[] = { "offset", "amount", NULL }; + int offset, amount; + + // offset and amount are expected, both optional + offset = amount = -1; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|ii", keywordList, + &offset, &amount)) + return NULL; + + if (ExternalLobVar_Verify(var) < 0) + return NULL; + return ExternalLobVar_Value(var, offset, amount); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Str() +// Return all of the data in the external LOB variable. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Str( + udt_ExternalLobVar *var) // variable to return the string for +{ + if (ExternalLobVar_Verify(var) < 0) + return NULL; + return ExternalLobVar_Value(var, 1, -1); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Write() +// Write a value to the LOB variable; return the number of bytes written. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Write( + udt_ExternalLobVar *var, // variable to perform write against + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + static char *keywordList[] = { "data", "offset", NULL }; + int offset, bufferLength, length; + char *buffer; + sword status; + + // buffer and offset are expected, offset is optional + offset = -1; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "s#|i", keywordList, + &buffer, &bufferLength, &offset)) + return NULL; + if (offset < 0) + offset = 1; + + // create a string for retrieving the value + if (ExternalLobVar_Verify(var) < 0) + return NULL; + length = bufferLength; + status = OCILobWrite(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, var->lobVar->data[var->pos], + (unsigned int*) &length, offset, buffer, bufferLength, + OCI_ONE_PIECE, NULL, NULL, 0, var->lobVar->type->charsetForm); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_Write()") < 0) + return NULL; + + // return the result + return PyInt_FromLong(length); +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Trim() +// Trim the LOB variable to the specified length. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Trim( + udt_ExternalLobVar *var, // variable to perform write against + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + static char *keywordList[] = { "newSize", NULL }; + sword status; + ub4 newSize; + + // buffer and offset are expected, offset is optional + newSize = 0; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, + &newSize)) + return NULL; + + // create a string for retrieving the value + if (ExternalLobVar_Verify(var) < 0) + return NULL; + status = OCILobTrim(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, var->lobVar->data[var->pos], + newSize); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_Trim()") < 0) + return NULL; + + // return the result + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_Reduce() +// Method provided for pickling/unpickling of LOB variables. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_Reduce( + udt_ExternalLobVar *self) // variable to dump +{ + PyObject *result, *value; + + value = ExternalLobVar_Str(self); + if (!value) + return NULL; + result = Py_BuildValue("(O(O))", &PyString_Type, value); + Py_DECREF(value); + return result; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_GetFileName() +// Return the directory alias and file name for the BFILE lob. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_GetFileName( + udt_ExternalLobVar *var, // variable to perform write against + PyObject *args) // arguments +{ + char dirAlias[30], name[255]; + ub2 dirAliasLength, nameLength; + PyObject *result, *temp; + sword status; + + // determine the directory alias and name + if (ExternalLobVar_Verify(var) < 0) + return NULL; + dirAliasLength = sizeof(dirAlias); + nameLength = sizeof(name); + status = OCILobFileGetName(var->lobVar->environment->handle, + var->lobVar->environment->errorHandle, var->lobVar->data[var->pos], + (text*) dirAlias, &dirAliasLength, (text*) name, &nameLength); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_GetFileName()") < 0) + return NULL; + + // create the two-tuple for returning + result = PyTuple_New(2); + if (!result) + return NULL; + temp = PyString_FromStringAndSize(dirAlias, dirAliasLength); + if (!temp) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, 0, temp); + temp = PyString_FromStringAndSize(name, nameLength); + if (!temp) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, 1, temp); + + return result; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_SetFileName() +// Set the directory alias and file name for the BFILE lob. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_SetFileName( + udt_ExternalLobVar *var, // variable to perform write against + PyObject *args) // arguments +{ + int dirAliasLength, nameLength; + char *dirAlias, *name; + sword status; + + // get the directory alias and name as strings + if (!PyArg_ParseTuple(args, "s#s#", &dirAlias, &dirAliasLength, &name, + &nameLength)) + return NULL; + + // create a string for retrieving the value + if (ExternalLobVar_Verify(var) < 0) + return NULL; + status = OCILobFileSetName(var->lobVar->environment->handle, + var->lobVar->environment->errorHandle, + &var->lobVar->data[var->pos], (text*) dirAlias, + (ub2) dirAliasLength, (text*) name, (ub2) nameLength); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_SetFileName()") < 0) + return NULL; + + // return the result + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// ExternalLobVar_FileExists() +// Return a boolean indicating if the BFIILE lob exists. +//----------------------------------------------------------------------------- +static PyObject *ExternalLobVar_FileExists( + udt_ExternalLobVar *var, // variable to perform write against + PyObject *args) // arguments +{ + PyObject *result; + sword status; + boolean flag; + + if (ExternalLobVar_Verify(var) < 0) + return NULL; + status = OCILobFileExists(var->lobVar->connection->handle, + var->lobVar->environment->errorHandle, var->lobVar->data[var->pos], + &flag); + if (Environment_CheckForError(var->lobVar->environment, status, + "ExternalLobVar_FileExists()") < 0) + return NULL; + + // return the result + if (flag) + result = Py_True; + else result = Py_False; + Py_INCREF(result); + return result; +} + diff --git a/ExternalObjectVar.c b/ExternalObjectVar.c new file mode 100644 index 0000000..0b6c1e9 --- /dev/null +++ b/ExternalObjectVar.c @@ -0,0 +1,286 @@ +//----------------------------------------------------------------------------- +// ExternalObjectVar.c +// Defines the routines for handling object variables external to this +// module. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// external Object type +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + PyObject *referencedObject; + udt_ObjectType *objectType; + dvoid *instance; + dvoid *indicator; + int isIndependent; +} udt_ExternalObjectVar; + + +//----------------------------------------------------------------------------- +// Declaration of external object variable functions. +//----------------------------------------------------------------------------- +static void ExternalObjectVar_Free(udt_ExternalObjectVar*); +static PyObject *ExternalObjectVar_GetAttr(udt_ExternalObjectVar*, PyObject*); +static PyObject *ExternalObjectVar_ConvertToPython(udt_Environment*, + OCITypeCode, dvoid*, dvoid*, PyObject*, udt_ObjectType*); + + +//----------------------------------------------------------------------------- +// Python type declaration +//----------------------------------------------------------------------------- +static PyTypeObject g_ExternalObjectVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.OBJECT", // tp_name + sizeof(udt_ExternalObjectVar), // tp_basicsize + 0, // tp_itemsize + (destructor) ExternalObjectVar_Free, + // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) ExternalObjectVar_GetAttr, + // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_New() +// Create a new external LOB variable. +//----------------------------------------------------------------------------- +PyObject *ExternalObjectVar_New( + PyObject *referencedObject, // referenced object + udt_ObjectType *objectType, // type of object + dvoid *instance, // object instance data + dvoid *indicator, // indicator structure + int isIndependent) // is object independent? +{ + udt_ExternalObjectVar *newVar; + + newVar = PyObject_NEW(udt_ExternalObjectVar, &g_ExternalObjectVarType); + if (!newVar) + return NULL; + Py_INCREF(referencedObject); + newVar->referencedObject = referencedObject; + Py_INCREF(objectType); + newVar->objectType = objectType; + newVar->instance = instance; + newVar->indicator = indicator; + newVar->isIndependent = isIndependent; + return (PyObject*) newVar; +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_Free() +// Free an external LOB variable. +//----------------------------------------------------------------------------- +static void ExternalObjectVar_Free( + udt_ExternalObjectVar *self) // variable to free +{ + if (self->isIndependent) + OCIObjectFree(self->objectType->environment->handle, + self->objectType->environment->errorHandle, + self->instance, OCI_OBJECTFREE_FORCE); + Py_DECREF(self->objectType); + Py_DECREF(self->referencedObject); + PyObject_DEL(self); +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_ConvertCollectionElements() +// Convert the collection elements to Python values. +//----------------------------------------------------------------------------- +static int ExternalObjectVar_ConvertCollectionElements( + udt_Environment *environment, // environment to use + OCIIter *iter, // iterator + PyObject *list, // list result + PyObject *referencedObject, // referenced object + udt_ObjectType *objectType) // collection type information +{ + dvoid *elementValue, *elementIndicator; + PyObject *elementObject; + boolean endOfCollection; + sword status; + + while (list) { + status = OCIIterNext(environment->handle, environment->errorHandle, + iter, &elementValue, &elementIndicator, &endOfCollection); + if (Environment_CheckForError(environment, status, + "ExternalObjectVar_ConvertCollection(): get next") < 0) + return -1; + if (endOfCollection) + break; + elementObject = ExternalObjectVar_ConvertToPython(environment, + objectType->elementTypeCode, elementValue, elementIndicator, + referencedObject, (udt_ObjectType*) objectType->elementType); + if (!elementObject) + return -1; + if (PyList_Append(list, elementObject) < 0) { + Py_DECREF(elementObject); + return -1; + } + Py_DECREF(elementObject); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_ConvertCollection() +// Convert a collection to a Python list. +//----------------------------------------------------------------------------- +static PyObject *ExternalObjectVar_ConvertCollection( + udt_Environment *environment, // environment to use + OCIColl *collectionValue, // collection value + PyObject *referencedObject, // referenced object + udt_ObjectType *objectType) // collection type information +{ + PyObject *list; + OCIIter *iter; + sword status; + int result; + + // create the iterator + status = OCIIterCreate(environment->handle, environment->errorHandle, + collectionValue, &iter); + if (Environment_CheckForError(environment, status, + "ExternalObjectVar_ConvertCollection(): creating iterator") < 0) + return NULL; + + // create the result list + list = PyList_New(0); + if (list) { + result = ExternalObjectVar_ConvertCollectionElements(environment, iter, + list, referencedObject, objectType); + if (result < 0) { + Py_DECREF(list); + list = NULL; + } + } + OCIIterDelete(environment->handle, environment->errorHandle, &iter); + + return list; +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_ConvertToPython() +// Convert an Oracle value to a Python value. +//----------------------------------------------------------------------------- +static PyObject *ExternalObjectVar_ConvertToPython( + udt_Environment *environment, // environment to use + OCITypeCode typeCode, // type of Oracle data + dvoid *value, // Oracle value + dvoid *indicator, // null indicator + PyObject *referencedObject, // referenced object (for sub objects) + udt_ObjectType *subType) // sub type (for sub objects) +{ + OraText *stringValue; + + // null values returned as None + if (* (OCIInd*) indicator == OCI_IND_NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + switch (typeCode) { + case OCI_TYPECODE_VARCHAR2: + stringValue = OCIStringPtr(environment->handle, + * (OCIString**) value); + return PyString_FromString((char*) stringValue); + case OCI_TYPECODE_NUMBER: + return OracleNumberToPythonFloat(environment, (OCINumber*) value); + case OCI_TYPECODE_DATE: + return OracleDateToPythonDate(&vt_DateTime, (OCIDate*) value); + case OCI_TYPECODE_OBJECT: + return ExternalObjectVar_New(referencedObject, subType, value, + indicator, 0); + case OCI_TYPECODE_NAMEDCOLLECTION: + return ExternalObjectVar_ConvertCollection(environment, + * (OCIColl**) value, referencedObject, subType); + }; + + return PyErr_Format(g_NotSupportedErrorException, + "ExternalObjectVar_GetAttributeValue(): unhandled data type %d", + typeCode); +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_GetAttributeValue() +// Retrieve an attribute on the external LOB variable object. +//----------------------------------------------------------------------------- +static PyObject *ExternalObjectVar_GetAttributeValue( + udt_ExternalObjectVar *self, // object + udt_ObjectAttribute *attribute) // attribute to get +{ + dvoid *valueIndicator, *value; + OCIInd scalarValueIndicator; + ub4 nameLength; + sword status; + OCIType *tdo; + char *name; + + // get the value for the attribute + name = PyString_AS_STRING(attribute->name); + nameLength = PyString_GET_SIZE(attribute->name); + status = OCIObjectGetAttr(self->objectType->environment->handle, + self->objectType->environment->errorHandle, self->instance, + self->indicator, self->objectType->tdo, (const OraText**) &name, + &nameLength, 1, 0, 0, &scalarValueIndicator, &valueIndicator, + &value, &tdo); + if (Environment_CheckForError(self->objectType->environment, status, + "ExternalObjectVar_GetAttributeValue(): getting value") < 0) + return NULL; + + // determine the proper null indicator + if (!valueIndicator) + valueIndicator = &scalarValueIndicator; + + return ExternalObjectVar_ConvertToPython(self->objectType->environment, + attribute->typeCode, value, valueIndicator, (PyObject*) self, + attribute->subType); +} + + +//----------------------------------------------------------------------------- +// ExternalObjectVar_GetAttr() +// Retrieve an attribute on the external LOB variable object. +//----------------------------------------------------------------------------- +static PyObject *ExternalObjectVar_GetAttr( + udt_ExternalObjectVar *self, // object + PyObject *nameObject) // name of attribute +{ + udt_ObjectAttribute *attribute; + char *name; + + attribute = (udt_ObjectAttribute*) + PyDict_GetItem(self->objectType->attributesByName, nameObject); + if (attribute) + return ExternalObjectVar_GetAttributeValue(self, attribute); + name = PyString_AS_STRING(nameObject); + if (name[0] == 't' && strcmp(name, "type") == 0) { + Py_INCREF(self->objectType); + return (PyObject*) self->objectType; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + diff --git a/HISTORY.txt b/HISTORY.txt new file mode 100644 index 0000000..4ecf854 --- /dev/null +++ b/HISTORY.txt @@ -0,0 +1,447 @@ +Changes from 4.3 to 4.3.1 + 1) Ensure that if the client buffer size exceeds 4000 bytes that the server + buffer size does not as strings may only contain 4000 bytes; this allows + handling of multibyte character sets on the server as well as the client. + 2) Added support for using buffer objects to populate binary data and made the + Binary() constructor the buffer type as requested by Ken Mason. + 3) Fix potential crash when using full optimization with some compilers. + Thanks to Aris Motas for noticing this and providing the initial patch and + to Amaury Forgeot d'Arc for providing an even simpler solution. + 4) Pass the correct charset form in to the write call in order to support + writing to national character set LOB values properly. Thanks to Ian Kelly + for noticing this discrepancy. + +Changes from 4.2.1 to 4.3 + 1) Added preliminary support for fetching Oracle objects (SQL types) as + requested by Kristof Beyls (who kindly provided an initial patch). + Additional work needs to be done to support binding and updating objects + but the basic structure is now in place. + 2) Added connection.maxBytesPerCharacter which indicates the maximum number of + bytes each character can use; use this value to also determine the size of + local buffers in order to handle discrepancies between the client character + set and the server character set. Thanks to Andreas Mock for providing the + initial patch and working with me to resolve this issue. + 3) Added support for querying native floats in Oracle 10g as requested by + Danny Boxhoorn. + 4) Add support for temporary LOB variables created via PL/SQL instead of only + directly by cx_Oracle; thanks to Henning von Bargen for discovering this + problem. + 5) Added support for specifying variable types using the builtin types int, + float, str and datetime.date which allows for finer control of what type of + Python object is returned from cursor.callfunc() for example. + 6) Added support for passing booleans to callproc() and callfunc() as + requested by Anana Aiyer. + 7) Fixed support for 64-bit environments in Python 2.5. + 8) Thanks to Filip Ballegeer and a number of his co-workers, an intermittent + crash was tracked down; specifically, if a connection is closed, then the + call to OCIStmtRelease() will free memory twice. Preventing the call when + the connection is closed solves the problem. + +Changes from 4.2 to 4.2.1 + 1) Added additional type (NCLOB) to handle CLOBs that use the national + character set as requested by Chris Dunscombe. + 2) Added support for returning cursors from functions as requested by Daniel + Steinmann. + 3) Added support for getting/setting the "get" mode on session pools as + requested by Anand Aiyer. + 4) Added support for binding subclassed cursors. + 5) Fixed binding of decimal objects with absolute values less than 0.1. + +Changes from 4.1.2 to 4.2 + 1) Added support for parsing an Oracle statement as requested by Patrick + Blackwill. + 2) Added support for BFILEs at the request of Matthew Cahn. + 3) Added support for binding decimal.Decimal objects to cursors. + 4) Added support for reading from NCLOBs as requested by Chris Dunscombe. + 5) Added connection attributes encoding and nencoding which return the IANA + character set name for the character set and national character set in use + by the client. + 6) Rework module initialization to use the techniques recommended by the + Python documentation as one user was experiencing random segfaults due + to the use of the module dictionary after the initialization was complete. + 7) Removed support for the OPT_Threading attribute. Use the threaded keyword + when creating connections and session pools instead. + 8) Removed support for the OPT_NumbersAsStrings attribute. Use the + numbersAsStrings attribute on cursors instead. + 9) Use type long rather than type int in order to support long integers on + 64-bit machines as reported by Uwe Hoffmann. +10) Add cursor attribute "bindarraysize" which is defaulted to 1 and is used + to determine the size of the arrays created for bind variables. +11) Added repr() methods to provide something a little more useful than the + standard type name and memory address. +12) Added keyword argument support to the functions that imply such in the + documentation as requested by Harald Armin Massa. +13) Treat an empty dictionary passed through to cursor.execute() as keyword + arguments the same as if no keyword arguments were specified at all, as + requested by Fabien Grumelard. +14) Fixed memory leak when a LOB read would fail. +15) Set the LDFLAGS value in the environment rather than directly in the + setup.py file in order to satisfy those who wish to enable the use of + debugging symbols. +16) Use __DATE__ and __TIME__ to determine the date and time of the build + rather than passing it through directly. +17) Use Oracle types and add casts to reduce warnings as requested by Amaury + Forgeot d'Arc. +18) Fixed typo in error message. + +Changes from 4.1.1 to 4.1.2 + 1) Restore support of Oracle 9i features when using the Oracle 10g client. + +Changes from 4.1 to 4.1.1 + 1) Add support for dropping a connection from a session pool. + 2) Add support for write only attributes "module", "action" and "clientinfo" + which work only in Oracle 10g as requested by Egor Starostin. + 3) Add support for pickling database errors. + 4) Use the previously created bind variable as a template if available when + creating a new variable of a larger size. Thanks to Ted Skolnick for the + initial patch. + 5) Fixed tests to work properly in the Python 2.4 environment where dates and + timestamps are different Python types. Thanks to Henning von Bargen for + pointing this out. + 6) Added additional directories to search for include files and libraries in + order to better support the Oracle 10g instant client. + 7) Set the internal fetch number to 0 in order to satisfy very picky source + analysis tools as requested by Amaury Fogeot d'Arc. + 8) Improve the documentation for building and installing the module from + source as some people are unaware of the standard methods for building + Python modules using distutils. + 9) Added note in the documentation indicating that the arraysize attribute + can drastically affect performance of queries since this seems to be a + common misunderstanding of first time users of cx_Oracle. +10) Add a comment indicating that on HP-UX Itanium with Oracle 10g the library + ttsh10 must alos be linked against. Thanks to Bernard Delmee for the + information. + +Changes from 4.1 beta 1 to 4.1 + 1) Fixed bug where subclasses of Cursor do not pass the connection in the + constructor causing a segfault. + 2) DDL statements must be reparsed before execution as noted by Mihai + Ibanescu. + 3) Add support for setting input sizes by position. + 4) Fixed problem with catching an exception during execute and then still + attempting to perform a fetch afterwards as noted by Leith Parkin. + 5) Rename the types so that they can be pickled and unpickled. Thanks to Harri + Pasanen for pointing out the problem. + 6) Handle invalid NLS_LANG setting properly (Oracle seems to like to provide a + handle back even though it is invalid) and determine the number of bytes + per character in order to allow for proper support in the future of + multibyte and variable width character sets. + 7) Remove date checking from the native case since Python already checks that + dates are valid; enhance error message when invalid dates are encountered + so that additional processing can be done. + 8) Fix bug executing SQL using numeric parameter names with predefined + variables (such as what takes place when calling stored procedures with out + parameters). + 9) Add support for reading CLOB values using multibyte or variable length + character sets. + +Changes from 4.0.1 to 4.1 beta 1 + 1) Added support for Python 2.4. In Python 2.4, the datetime module is used + for both binding and fetching of date and timestamp data. In Python 2.3, + objects from the datetime module can be bound but the internal datetime + objects will be returned from queries. + 2) Added pickling support for LOB and datetime data. + 3) Fully qualified the table name that was missing in an alter table + statement in the setup test script as noted by Marc Gehling. + 4) Added a section allowing for the setting of the RPATH linker directive in + setup.py as requested by Iustin Pop. + 5) Added code to raise a programming error exception when an attempt is made + to access a LOB locator variable in a subsequent fetch. + 6) The username, password and dsn (tnsentry) are stored on the connection + object when specified, regardless of whether or not a standard connection + takes place. + 7) Added additional module level constant called "LOB" as requested by Joseph + Canedo. + 8) Changed exception type to IntegrityError for constraint violations as + requested by Joseph Canedo. + 9) If scale and precision are not specified, an attempt is made to return a + long integer as requested by Joseph Canedo. +10) Added workaround for Oracle bug which returns an invalid handle when the + prepare call fails. Thanks to alantam@hsbc.com for providing the code that + demonstrated the problem. +11) The cusor method arravar() will now accept the actual list so that it is + not necessary to call cursor.arrayvar() followed immediately by + var.setvalue(). +12) Fixed bug where attempts to execute the statement "None" with bind + variables would cause a segmentation fault. +13) Added support for binding by position (paramstyle = "numeric"). +14) Removed memory leak created by calls to OCIParamGet() which were not + mirrored by calls to OCIDescriptorFree(). Thanks to Mihai Ibanescu for + pointing this out and providing a patch. +15) Added support for calling cursor.executemany() with statement None + implying that the previously prepared statement ought to be executed. + Thanks to Mihai Ibanescu for providing a patch. +16) Added support for rebinding variables when a subsequent call to + cursor.executemany() uses a different number of rows. Thanks to Mihai + Ibanescu for supplying a patch. +17) The microseconds are now displayed in datetime variables when nonzero + similar to method used in the datetime module. +18) Added support for binary_float and binary_double columns in Oracle 10g. + +Changes from 4.0 to 4.0.1 + 1) Fixed bugs on 64-bit platforms that caused segmentation faults and bus + errors in session pooling and determining the bind variables associated + with a statement. + 2) Modified test suite so that 64-bit platforms are tested properly. + 3) Added missing commit statements in the test setup scripts. Thanks to Keith + Lyon for pointing this out. + 4) Fix setup.py for Cygwin environments. Thanks to Doug Henderson for + providing the necessary fix. + 5) Added support for compiling cx_Oracle without thread support. Thanks to + Andre Reitz for pointing this out. + 6) Added support for a new keyword parameter called threaded on connections + and session pools. This parameter defaults to False and indicates whether + threaded mode ought to be used. It replaces the module level attribute + OPT_Threading although examining the attribute will be retained until the + next release at least. + 7) Added support for a new keyword parameter called twophase on connections. + This parameter defaults to False and indicates whether support for two + phase (distributed or global) transactions ought to be present. Note that + support for distributed transactions is buggy when crossing major version + boundaries (Oracle 8i to Oracle 9i for example). + 8) Ensure that the rowcount attribute is set properly when an exception is + raised during execution. Thanks to Gary Aviv for pointing out this problem + and its solution. + +Changes from 3.1 to 4.0 + 1) Added support for subclassing connections, cursors and session pools. The + changes involved made it necessary to drop support for Python 2.1 and + earlier although a branch exists in CVS to allow for support of Python 2.1 + and earlier if needed. + 2) Connections and session pools can now be created with keyword parameters, + not just sequential parameters. + 3) Queries now return integers whenever possible and long integers if the + number will overflow a simple integer. Floats are only returned when it is + known that the number is a floating point number or the integer conversion + fails. + 4) Added initial support for user callbacks on OCI functions. See the + documentation for more details. + 5) Add support for retrieving the bind variable names associated with a + cursor with a new method bindnames(). + 6) Add support for temporary LOB variables. This means that setinputsizes() + can be used with the values CLOB and BLOB to create these temporary LOB + variables and allow for the equivalent of empty_clob() and empty_blob() + since otherwise Oracle will treat empty strings as NULL values. + 7) Automatically switch to long strings when the data size exceeds the + maximum string size that Oracle allows (4000 characters) and raise an + error if an attempt is made to set a string variable to a size that it + does not support. This avoids truncation errors as reported by Jon Franz. + 8) Add support for global (distributed) transactions and two phase commit. + 9) Force the NLS settings for the session so that test tables are populated + correctly in all circumstances; problems were noted by Ralf Braun and + Allan Poulsen. +10) Display error messages using the environment handle when the error handle + has not yet been created; this provides better error messages during this + rather rare situation. +11) Removed memory leak in callproc() that was reported by Todd Whiteman. +12) Make consistent the calls to manipulate memory; otherwise segfaults can + occur when the pymalloc option is used, as reported by Matt Hoskins. +13) Force a rollback when a session is released back to the session pool. + Apparently the connections are not as stateless as Oracle's documentation + suggests and this makes the logic consistent with normal connections. +14) Removed module method attach(). This can be replaced with a call to + Connection(handle=) if needed. + +Changes from 3.0a to 3.1 + 1) Added support for connecting with SYSDBA and SYSOPER access which is + needed for connecting as sys in Oracle 9i. + 2) Only check the dictionary size if the variable is not NULL; otherwise, an + error takes place which is not caught or cleared; this eliminates a + spurious "Objects/dictobject.c:1258: bad argument to internal function" in + Python 2.3. + 3) Add support for session pooling. This is only support for Oracle 9i but + is amazingly fast -- about 100 times faster than connecting. + 4) Add support for statement caching when pooling sessions, this reduces the + parse time considerably. Unfortunately, the Oracle OCI does not allow this + to be easily turned on for normal sessions. + 5) Add method trim() on CLOB and BLOB variables for trimming the size. + 6) Add support for externally identified users; to use this feature leave the + username and password fields empty when connecting. + 7) Add method cancel() on connection objects to cancel long running queries. + Note that this only works on non-Windows platforms. + 8) Add method callfunc() on cursor objects to allow calling a function + without using an anonymous PL/SQL block. + 9) Added documentation on objects that were not documented. At this point all + objects, methods and constants in cx_Oracle have been documented. +10) Added support for timestamp columns in Oracle 9i. +11) Added module level method makedsn() which creates a data source name given + the host, port and SID. +12) Added constant "buildtime" which is the time when the module was built as + an additional means of identifying the build that is in use. +13) Binding a value that is incompatible to the previous value that was bound + (data types do not match or array size is larger) will now result in a + new bind taking place. This is more consistent with the DB API although + it does imply a performance penalty when used. + +Changes from 3.0 to 3.0a + 1) Fixed bug where zero length PL/SQL arrays were being mishandled + 2) Fixed support for the data type "float" in Oracle; added one to the + display size to allow for the sign of the number, if necessary; changed + the display size of unconstrained numbers to 127, which is the largest + number that Oracle can handle + 3) Added support for retrieving the description of a bound cursor before + fetching it + 4) Fixed a couple of build issues on Mac OS X, AIX and Solaris (64-bit) + 5) Modified documentation slightly based on comments from several people + 6) Included files in MANIFEST that are needed to generate the binaries + 7) Modified test suite to work within the test environment at Computronix + as well as within the packages that are distributed + +Changes from 2.5a to 3.0 + 1) Removed support for connection to Oracle7 databases; it is entirely + possible that it will still work but I no longer have any way of testing + and Oracle has dropped any meaningful support for Oracle7 anyway + 2) Fetching of strings is now done with predefined memory areas rather than + dynamic memory areas; dynamic fetching of strings was causing problems + with Oracle 9i in some instances and databases using a different character + set other than US ASCII + 3) Fixed bug where segfault would occur if the '/' character preceded the '@' + character in a connect string + 4) Added two new cursor methods var() and arrayvar() in order to eliminate + the need for setinputsizes() when defining PL/SQL arrays and as a generic + method of acquiring bind variables directly when needed + 5) Fixed support for binding cursors and added support for fetching cursors + (these are known as ref cursors in PL/SQL). + 6) Eliminated discrepancy between the array size used internally and the + array size specified by the interface user; this was done earlier to avoid + bus errors on 64-bit platforms but another way has been found to get + around that issue and a number of people were getting confused because of + the discrepancy + 7) Added support for the attribute "connection" on cursors, an optional + DB API extension + 8) Added support for passing a dictionary as the second parameter for the + cursor.execute() method in order to comply with the DB API more closely; + the method of passing parameters with keyword arguments is still supported + and is in fact preferred + 9) Added support for the attribute "statement" on cursors which is a + reference to the last SQL statement prepared or executed +10) Added support for passing any sequence to callproc() rather than just + lists as before +11) Fixed bug where segfault would occur if the array size was changed after + the cursor was executed but before it was fetched +12) Ignore array size when performing executemany() and use the length of the + list of arguments instead +13) Rollback when connection is closed or destroyed to follow DB API rather + than use the Oracle default (which is commit) +14) Added check for array size too large causing an integer overflow +15) Added support for iterators for Python 2.2 and above +16) Added test suite based on PyUnitTest +17) Added documentation in HTML format similar to the documentation for the + core Python library + +Changes from 2.5 to 2.5a + 1) Fix problem with Oracle 9i and retrieving strings; it seems that Oracle 9i + uses the correct method for dynamic callback but Oracle 8i will not work + with that method so an #ifdef was added to check for the existence of an + Oracle 9i feature; thanks to Paul Denize for discovering this problem + +Changes from 2.4 to 2.5 + 1) Added flag OPT_NoOracle7 which, if set, assumes that connections are being + made to Oracle8 or higher databases; this allows for eliminating the + overhead in performing this check at connect time + 2) Added flag OPT_NumbersAsStrings which, if set, returns all numbers as + strings rather than integers or floats; this flag is used when defined + variables are created (during select statements only) + 3) Added flag OPT_Threading which, if set, uses OCI threading mode; there is a + significant performance degradation in this mode (about 15-20%) but it does + allow threads to share connections (threadsafety level 2 according to the + Python Database API 2.0); note that in order to support this, Oracle 8i or + higher is now required + 4) Added Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS pairs where + applicable to support threading during blocking OCI calls + 5) Added global method attach() to cx_Oracle to support attaching to an + existing database handle (as provided by PowerBuilder, for example) + 6) Eliminated the cursor method fetchbinds() which was used for returning the + list of bind variables after execution to get the values of out variables; + the cursor method setinputsizes() was modified to return the list of bind + variables and the cursor method execute() was modified to return the list + of defined variables in the case of a select statement being executed; + these variables have three methods available to them: getvalue([]) to + get the value of a variable, setvalue(, ) to set its value and + copy(, , ) to copy the value from a variable in a + more efficient manner than setvalue(getvalue()) + 7) Implemented cursor method executemany() which expects a list of + dictionaries for the arguments + 8) Implemented cursor method callproc() + 9) Added cursr method prepare() which parses (prepares) the statement for + execution; subsequent execute() or executemany() calls can pass None as the + statement which will imply use of the previously prepared statement; used + for high performance only +10) Added cursor method fetchraw() which will perform a raw fetch of the cursor + returning the number of rows thus fetched; this is used to avoid the + overhead of generating result sets; used for high performance only +11) Added cursor method executemanyprepared() which is identical to the method + executemany() except that it takes a single argument which is the number of + times to execute a previously prepared statement and it assumes that the + bind variables already have their values set; used for high performance + only +12) Added support for rowid being returned in a select statement +13) Added support for comparing dates returned by cx_Oracle +14) Integrated patch from Andre Reitz to set the null ok flag in the + description attribute of the cursor +15) Integrated patch from Andre Reitz to setup.py to support compilation with + Python 1.5 +16) Integrated patch from Benjamin Kearns to setup.py to support compilation + on Cygwin + +Changes from 2.3 to 2.4 + +1) String variables can now be made any length (previously restricted to the + 64K limit imposed by Oracle for default binding); use the type + cx_Oracle.LONG_STRING as the argument to setinputsizes() for binding in + string values larger than 4000 bytes. +2) Raw and long raw columns are now supported; use the types cx_Oracle.BINARY + and cx_Oracle.LONG_BINARY as the argument to setinputsizes() for binding in + values of these types. +3) Functions DateFromTicks(), TimeFromTicks() and TimestampFromTicks() + are now implemented. +4) Function cursor.setoutputsize() implemented +5) Added the ability to bind arrays as out parameters to procedures; use the + format [cx_Oracle., ] as the input to the function + setinputsizes() for binding arrays +6) Discovered from the Oracle 8.1.6 version of the documentation of the OCI + libraries, that the size of the memory location required for the precision + variable is larger than the printed documentation says; this was causing a + problem with the code on the Sun platform. +7) Now support building RPMs for Linux. + +Changes from 2.2 to 2.3 + + 1) Incremental performance enhancements (dealing with reusing cursors and + bind handles) + 2) Ensured that arrays of integers with a single float in them are all + treated as floats, as suggested by Martin Koch. + 3) Fixed code dealing with scale and precision for both defining a numeric + variable and for providing the cursor description; this eliminates the + problem of an underflow error (OCI-22054) when retrieving data with + non-zero scale. + +Changes from 2.1 to 2.2 + + 1) Upgraded thread safety to level 1 (according to the Python DB API 2.0) as + an internal project required the ability to share the module between + threads. + 2) Added ability to bind ref cursors to PL/SQL blocks as requested by + Brad Powell. + 3) Added function write(Value, [Offset]) to LOB variables as requested by + Matthias Kirst. + 4) Procedure execute() on Cursor objects now permits a value None for the + statement which means that the previously prepared statement will be + executed and any input sizes set earlier will be retained. This was done to + improve the performance of scripts that execute one statement many times. + 5) Modified module global constants BINARY and DATETIME to point to the + external representations of those types so that the expression + type(var) == cx_Oracle.DATETIME will work as expected. + 6) Added global constant version to provide means of determining the current + version of the module. + 7) Modified error checking routine to distinguish between an Oracle error and + invalid handles. + 8) Added error checking to avoid setting the value of a bind variable to a + value that it cannot support and raised an exception to indicate this fact. + 9) Added extra compile arguments for the AIX platform as suggested by Jehwan + Ryu. +10) Added section to the README to indicate the method for a binary + installation as suggested by Steve Holden. +11) Added simple usage example as requested by many people. +12) Added HISTORY file to the distribution. + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..2530600 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,35 @@ +Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. +All rights reserved. + +License for cx_Oracle + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions, and the disclaimer that follows. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Computronix nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +DISCLAIMER: +THIS SOFTWARE IS PROVIDED BY COMPUTRONIX AND CONTRIBUTORS *AS IS* +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COMPUTRONIX +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Computronix® is a registered trademark of Computronix (Canada) Ltd. + diff --git a/LobVar.c b/LobVar.c new file mode 100644 index 0000000..39bf006 --- /dev/null +++ b/LobVar.c @@ -0,0 +1,333 @@ +//----------------------------------------------------------------------------- +// LobVar.c +// Defines the routines for handling LOB variables. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// LOB type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + OCILobLocator **data; + udt_Connection *connection; + int isFile; +} udt_LobVar; + +#include "ExternalLobVar.c" + +//----------------------------------------------------------------------------- +// Declaration of LOB variable functions. +//----------------------------------------------------------------------------- +static int LobVar_Initialize(udt_LobVar*, udt_Cursor*); +static void LobVar_Finalize(udt_LobVar*); +static PyObject *LobVar_GetValue(udt_LobVar*, unsigned); +static int LobVar_SetValue(udt_LobVar*, unsigned, PyObject*); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_CLOBVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.CLOB", // tp_name + sizeof(udt_LobVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_NCLOBVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.NCLOB", // tp_name + sizeof(udt_LobVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_BLOBVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.BLOB", // tp_name + sizeof(udt_LobVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_BFILEVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.BFILE", // tp_name + sizeof(udt_LobVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_CLOB = { + (InitializeProc) LobVar_Initialize, + (FinalizeProc) LobVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LobVar_SetValue, + (GetValueProc) LobVar_GetValue, + &g_CLOBVarType, // Python type + SQLT_CLOB, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCILobLocator*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +static udt_VariableType vt_NCLOB = { + (InitializeProc) LobVar_Initialize, + (FinalizeProc) LobVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LobVar_SetValue, + (GetValueProc) LobVar_GetValue, + &g_NCLOBVarType, // Python type + SQLT_CLOB, // Oracle type + SQLCS_NCHAR, // charset form + sizeof(OCILobLocator*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +static udt_VariableType vt_BLOB = { + (InitializeProc) LobVar_Initialize, + (FinalizeProc) LobVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LobVar_SetValue, + (GetValueProc) LobVar_GetValue, + &g_BLOBVarType, // Python type + SQLT_BLOB, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCILobLocator*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +static udt_VariableType vt_BFILE = { + (InitializeProc) LobVar_Initialize, + (FinalizeProc) LobVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LobVar_SetValue, + (GetValueProc) LobVar_GetValue, + &g_BFILEVarType, // Python type + SQLT_BFILE, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCILobLocator*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +//----------------------------------------------------------------------------- +// LobVar_Initialize() +// Initialize the variable. +//----------------------------------------------------------------------------- +static int LobVar_Initialize( + udt_LobVar *var, // variable to initialize + udt_Cursor *cursor) // cursor created by +{ + sword status; + ub4 i; + + // initialize members + Py_INCREF(cursor->connection); + var->connection = cursor->connection; + var->isFile = (var->type == &vt_BFILE); + + // initialize the LOB locators + for (i = 0; i < var->allocatedElements; i++) { + status = OCIDescriptorAlloc(var->environment->handle, + (dvoid**) &var->data[i], OCI_DTYPE_LOB, 0, 0); + if (Environment_CheckForError(var->environment, status, + "LobVar_Initialize()") < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// LobVar_Finalize() +// Prepare for variable destruction. +//----------------------------------------------------------------------------- +static void LobVar_Finalize( + udt_LobVar *var) // variable to free +{ + boolean isTemporary; + ub4 i; + + for (i = 0; i < var->allocatedElements; i++) { + if (var->data[i]) { + OCILobIsTemporary(var->environment->handle, + var->environment->errorHandle, var->data[i], &isTemporary); + if (isTemporary) + OCILobFreeTemporary(var->connection->handle, + var->environment->errorHandle, var->data[i]); + OCIDescriptorFree(var->data[i], OCI_DTYPE_LOB); + } + } + Py_DECREF(var->connection); +} + + +//----------------------------------------------------------------------------- +// LobVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *LobVar_GetValue( + udt_LobVar *var, // variable to determine value for + unsigned pos) // array position +{ + return ExternalLobVar_New(var, pos); +} + + +//----------------------------------------------------------------------------- +// LobVar_SetValue() +// Sets the value stored at the given array position. +//----------------------------------------------------------------------------- +static int LobVar_SetValue( + udt_LobVar *var, // variable to determine value for + unsigned pos, // array position + PyObject *value) // value to set +{ + boolean isTemporary; + sword status; + ub1 lobType; + ub4 length; + + // only support strings + if (!PyString_Check(value)) { + PyErr_SetString(PyExc_TypeError, "expecting string data"); + return -1; + } + + // make sure have temporary LOBs set up + status = OCILobIsTemporary(var->environment->handle, + var->environment->errorHandle, var->data[pos], &isTemporary); + if (Environment_CheckForError(var->environment, status, + "LobVar_SetValue(): is temporary?") < 0) + return -1; + if (!isTemporary) { + if (var->type->oracleType == SQLT_BLOB) + lobType = OCI_TEMP_BLOB; + else lobType = OCI_TEMP_CLOB; + status = OCILobCreateTemporary(var->connection->handle, + var->environment->errorHandle, var->data[pos], OCI_DEFAULT, + OCI_DEFAULT, lobType, FALSE, OCI_DURATION_SESSION); + if (Environment_CheckForError(var->environment, status, + "LobVar_SetValue(): create temporary") < 0) + return -1; + } + + // trim the current value + status = OCILobTrim(var->connection->handle, + var->environment->errorHandle, var->data[pos], 0); + if (Environment_CheckForError(var->environment, status, + "LobVar_SetValue(): trim") < 0) + return -1; + + // set the current value + length = PyString_GET_SIZE(value); + if (length) { + status = OCILobWrite(var->connection->handle, + var->environment->errorHandle, var->data[pos], + &length, 1, PyString_AS_STRING(value), + PyString_GET_SIZE(value), OCI_ONE_PIECE, NULL, NULL, 0, 0); + if (Environment_CheckForError(var->environment, status, + "LobVar_SetValue(): write") < 0) + return -1; + } + + return 0; +} + diff --git a/LongVar.c b/LongVar.c new file mode 100644 index 0000000..6944b66 --- /dev/null +++ b/LongVar.c @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// LongVar.c +// Defines the routines specific to the long type. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// long type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + char *data; +} udt_LongVar; + + +//----------------------------------------------------------------------------- +// declaration of long variable functions. +//----------------------------------------------------------------------------- +static int LongVar_SetValue(udt_LongVar*, unsigned, PyObject*); +static PyObject *LongVar_GetValue(udt_LongVar*, unsigned); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_LongStringVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.LONG_STRING", // tp_name + sizeof(udt_LongVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_LongBinaryVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.LONG_BINARY", // tp_name + sizeof(udt_LongVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_LongString = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LongVar_SetValue, + (GetValueProc) LongVar_GetValue, + &g_LongStringVarType, // Python type + SQLT_LVC, // Oracle type + SQLCS_IMPLICIT, // charset form + 128 * 1024, // element length (default) + 1, // is variable length + 1, // can be copied + 0 // can be in array +}; + + +static udt_VariableType vt_LongBinary = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) LongVar_SetValue, + (GetValueProc) LongVar_GetValue, + &g_LongBinaryVarType, // Python type + SQLT_LVB, // Oracle type + SQLCS_IMPLICIT, // charset form + 128 * 1024, // element length (default) + 1, // is variable length + 1, // can be copied + 0 // can be in array +}; + + +//----------------------------------------------------------------------------- +// LongVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int LongVar_SetValue( + udt_LongVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + Py_ssize_t bufferSize; + const void *buffer; + char *ptr; + + // get the buffer data and size for binding + if (PyString_Check(value)) { + buffer = PyString_AS_STRING(value); + bufferSize = PyString_GET_SIZE(value); + } else if (PyBuffer_Check(value)) { + if (PyObject_AsReadBuffer(value, &buffer, &bufferSize) < 0) + return -1; + } else { + PyErr_SetString(PyExc_TypeError, "expecting string or buffer data"); + return -1; + } + + // copy the string to the Oracle buffer + if (bufferSize + sizeof(ub4) > var->maxLength) { + if (Variable_Resize( (udt_Variable*) var, + bufferSize + sizeof(ub4)) < 0) + return -1; + } + ptr = var->data + var->maxLength * pos; + *((ub4 *) ptr) = (ub4) bufferSize; + if (bufferSize) + memcpy(ptr + sizeof(ub4), buffer, bufferSize); + + return 0; +} + + +//----------------------------------------------------------------------------- +// LongVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *LongVar_GetValue( + udt_LongVar *var, // variable to determine value for + unsigned pos) // array position +{ + char *ptr; + ub4 size; + + ptr = var->data + var->maxLength * pos; + size = *((ub4 *) ptr); + return PyString_FromStringAndSize(ptr + sizeof(ub4), size); +} + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..558a55d --- /dev/null +++ b/MANIFEST @@ -0,0 +1,66 @@ +setup.py +setup.cfg +README.txt +LICENSE.txt +HISTORY.txt +MANIFEST +Callback.c +Connection.c +Cursor.c +CursorVar.c +cx_Oracle.c +DateTimeVar.c +Environment.c +Error.c +ExternalDateTimeVar.c +ExternalLobVar.c +ExternalObjectVar.c +LobVar.c +LongVar.c +NumberVar.c +ObjectType.c +ObjectVar.c +SessionPool.c +StringVar.c +TimestampVar.c +Transforms.c +Variable.c +html/about.html +html/blank.png +html/connobj.html +html/contents.html +html/contents.png +html/cursorobj.html +html/cx_Oracle.css +html/cx_Oracle.html +html/dateobj.html +html/front.html +html/index.html +html/index.png +html/lobobj.html +html/module.html +html/modules.png +html/next.png +html/node12.html +html/node4.html +html/node5.html +html/previous.png +html/sesspool.html +html/up.png +html/varobj.html +test/Connection.py +test/Cursor.py +test/CursorVar.py +test/DateTimeVar.py +test/LobVar.py +test/LongVar.py +test/NumberVar.py +test/ObjectVar.py +test/SessionPool.py +test/SetupTest_9i.sql +test/SetupTest.sql +test/StringVar.py +test/test_dbapi20.py +test/TestEnv.py +test/test.py +test/TimestampVar.py diff --git a/NumberVar.c b/NumberVar.c new file mode 100644 index 0000000..5a7fc33 --- /dev/null +++ b/NumberVar.c @@ -0,0 +1,508 @@ +//----------------------------------------------------------------------------- +// NumberVar.c +// Defines the routines for handling numeric variables. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// define number format used for acquiring long integers +//----------------------------------------------------------------------------- +static const char gc_NumberFormat[63] = + "999999999999999999999999999999999999999999999999999999999999999"; + +//----------------------------------------------------------------------------- +// Number type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + OCINumber *data; +} udt_NumberVar; + + +//----------------------------------------------------------------------------- +// Declaration of number variable functions. +//----------------------------------------------------------------------------- +static int NumberVar_PreDefine(udt_NumberVar*, OCIParam*); +static int NumberVar_SetValue(udt_NumberVar*, unsigned, PyObject*); +static PyObject *NumberVar_GetValue(udt_NumberVar*, unsigned); +#ifdef SQLT_BFLOAT +static int NumberVar_SetValueAsDouble(udt_NumberVar*, unsigned, PyObject*); +static PyObject *NumberVar_GetValueAsDouble(udt_NumberVar*, unsigned); +#endif + + +//----------------------------------------------------------------------------- +// Python type declaration +//----------------------------------------------------------------------------- +static PyTypeObject g_NumberVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.NUMBER", // tp_name + sizeof(udt_NumberVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_Float = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NumberVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValue, + (GetValueProc) NumberVar_GetValue, + &g_NumberVarType, // Python type + SQLT_VNU, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCINumber), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +#ifdef SQLT_BFLOAT +static udt_VariableType vt_NativeFloat = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValueAsDouble, + (GetValueProc) NumberVar_GetValueAsDouble, + &g_NumberVarType, // Python type + SQLT_BDOUBLE, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(double), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; +#endif + + +static udt_VariableType vt_Integer = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NumberVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValue, + (GetValueProc) NumberVar_GetValue, + &g_NumberVarType, // Python type + SQLT_VNU, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCINumber), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_LongInteger = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NumberVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValue, + (GetValueProc) NumberVar_GetValue, + &g_NumberVarType, // Python type + SQLT_VNU, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCINumber), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_NumberAsString = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NumberVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValue, + (GetValueProc) NumberVar_GetValue, + &g_NumberVarType, // Python type + SQLT_VNU, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCINumber), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_Boolean = { + (InitializeProc) NULL, + (FinalizeProc) NULL, + (PreDefineProc) NumberVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) NumberVar_SetValue, + (GetValueProc) NumberVar_GetValue, + &g_NumberVarType, // Python type + SQLT_VNU, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCINumber), // element length + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +//----------------------------------------------------------------------------- +// NumberVar_PreDefine() +// Set the type of value (integer, float or string) that will be returned +// when values are fetched from this variable. +//----------------------------------------------------------------------------- +static int NumberVar_PreDefine( + udt_NumberVar *var, // variable to initialize + OCIParam *param) // parameter handle +{ + sb2 precision; + sword status; + sb1 scale; + + // if the return type has not already been specified, check to see if the + // number can fit inside an integer by looking at the precision and scale + if (var->type == &vt_Float) { + scale = 0; + precision = 0; + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &scale, 0, + OCI_ATTR_SCALE, var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "NumberVar_PreDefine(): scale") < 0) + return -1; + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &precision, 0, + OCI_ATTR_PRECISION, var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "NumberVar_PreDefine(): precision") < 0) + return -1; + if (scale == 0 && precision > 0 && precision < 10) + var->type = &vt_Integer; + else if (scale == 0 || (scale == -127 && precision == 0)) + var->type = &vt_LongInteger; + } + + return 0; +} + + +#if (PY_VERSION_HEX >= 0x02030000) +//----------------------------------------------------------------------------- +// NumberVar_SetValueFromBoolean() +// Set the value of the variable from a Python boolean. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueFromBoolean( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + long integerValue; + sword status; + + integerValue = (value == Py_True); + status = OCINumberFromInt(var->environment->errorHandle, &integerValue, + sizeof(long), OCI_NUMBER_SIGNED, &var->data[pos]); + return Environment_CheckForError(var->environment, status, + "NumberVar_SetValueFromBoolean()"); +} +#endif + + +//----------------------------------------------------------------------------- +// NumberVar_SetValueFromInteger() +// Set the value of the variable from a Python integer. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueFromInteger( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + long integerValue; + sword status; + + integerValue = PyInt_AS_LONG(value); + status = OCINumberFromInt(var->environment->errorHandle, &integerValue, + sizeof(long), OCI_NUMBER_SIGNED, &var->data[pos]); + return Environment_CheckForError(var->environment, status, + "NumberVar_SetValueFromInteger()"); +} + + +//----------------------------------------------------------------------------- +// NumberVar_SetValueFromFloat() +// Set the value of the variable from a Python float. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueFromFloat( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + double doubleValue; + sword status; + + doubleValue = PyFloat_AS_DOUBLE(value); + status = OCINumberFromReal(var->environment->errorHandle, &doubleValue, + sizeof(double), &var->data[pos]); + return Environment_CheckForError(var->environment, status, + "NumberVar_SetValueFromFloat()"); +} + + +//----------------------------------------------------------------------------- +// NumberVar_SetValueFromLong() +// Set the value of the variable from a Python long. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueFromLong( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + PyObject *textValue; + sword status; + + textValue = PyObject_Str(value); + if (!textValue) + return -1; + status = OCINumberFromText(var->environment->errorHandle, + (OraText*) PyString_AS_STRING(textValue), + PyString_GET_SIZE(textValue), (OraText*) gc_NumberFormat, + sizeof(gc_NumberFormat), NULL, 0, &var->data[pos]); + Py_DECREF(textValue); + return Environment_CheckForError(var->environment, status, + "NumberVar_SetValueFromLong()"); +} + + +//----------------------------------------------------------------------------- +// NumberVar_GetFormatAndTextFromDecimal() +// Return the number format and text to use for the Decimal object. +//----------------------------------------------------------------------------- +static int NumberVar_GetFormatAndTextFromDecimal( + PyObject *tupleValue, // decimal as_tuple() value + char **textValue, // text string for conversion + char **format) // format for conversion +{ + long numDigits, scale, i, sign, length, digit; + char *valuePtr, *formatPtr; + PyObject *digits; + + // acquire basic information from the value tuple + sign = PyInt_AS_LONG(PyTuple_GET_ITEM(tupleValue, 0)); + digits = PyTuple_GET_ITEM(tupleValue, 1); + scale = PyInt_AS_LONG(PyTuple_GET_ITEM(tupleValue, 2)); + numDigits = PyTuple_GET_SIZE(digits); + + // allocate memory for the string and format to use in conversion + length = numDigits + abs(scale) + 3; + *textValue = valuePtr = PyMem_Malloc(length); + if (!*textValue) { + PyErr_NoMemory(); + return -1; + } + *format = formatPtr = PyMem_Malloc(length); + if (!*format) { + PyMem_Free(*textValue); + PyErr_NoMemory(); + return -1; + } + + // populate the string and format + if (sign) + *valuePtr++ = '-'; + for (i = 0; i < numDigits + scale; i++) { + *formatPtr++ = '9'; + if (i < numDigits) + digit = PyInt_AS_LONG(PyTuple_GetItem(digits, i)); + else digit = 0; + *valuePtr++ = '0' + (char) digit; + } + if (scale < 0) { + *formatPtr++ = 'D'; + *valuePtr++ = '.'; + for (i = scale; i < 0; i++) { + *formatPtr++ = '9'; + if (numDigits + i < 0) + digit = 0; + else digit = PyInt_AS_LONG(PyTuple_GetItem(digits, numDigits + i)); + *valuePtr++ = '0' + (char) digit; + } + } + *formatPtr = '\0'; + *valuePtr = '\0'; + return 0; +} + + +//----------------------------------------------------------------------------- +// NumberVar_SetValueFromDecimal() +// Set the value of the variable from a Python decimal.Decimal object. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueFromDecimal( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + char *textValue, *format; + PyObject *tupleValue; + sword status; + + tupleValue = PyObject_CallMethod(value, "as_tuple", NULL); + if (!tupleValue) + return -1; + if (NumberVar_GetFormatAndTextFromDecimal(tupleValue, &textValue, + &format) < 0) { + Py_DECREF(tupleValue); + return -1; + } + status = OCINumberFromText(var->environment->errorHandle, + (OraText*) textValue, strlen(textValue), (OraText*) format, + strlen(format), NULL, 0, &var->data[pos]); + Py_DECREF(tupleValue); + PyMem_Free(textValue); + PyMem_Free(format); + return Environment_CheckForError(var->environment, status, + "NumberVar_SetValueFromDecimal()"); +} + + +//----------------------------------------------------------------------------- +// NumberVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int NumberVar_SetValue( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + if (PyInt_Check(value)) + return NumberVar_SetValueFromInteger(var, pos, value); +#if (PY_VERSION_HEX >= 0x02030000) + else if (PyBool_Check(value)) + return NumberVar_SetValueFromBoolean(var, pos, value); +#endif + else if (PyFloat_Check(value)) + return NumberVar_SetValueFromFloat(var, pos, value); + else if (PyLong_Check(value)) + return NumberVar_SetValueFromLong(var, pos, value); + else if (value->ob_type == g_DecimalType) + return NumberVar_SetValueFromDecimal(var, pos, value); + PyErr_SetString(PyExc_TypeError, "expecting numeric data"); + return -1; +} + + +#ifdef SQLT_BFLOAT +//----------------------------------------------------------------------------- +// NumberVar_SetValueAsDouble() +// Set the value of the variable which should be a native double. +//----------------------------------------------------------------------------- +static int NumberVar_SetValueAsDouble( + udt_NumberVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + double *doublePtr; + + if (!PyFloat_Check(value)) { + PyErr_SetString(PyExc_TypeError, "expecting float"); + return -1; + } + + doublePtr = (double*) &var->data[pos]; + *doublePtr = PyFloat_AS_DOUBLE(value); + return 0; +} +#endif + + +//----------------------------------------------------------------------------- +// NumberVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *NumberVar_GetValue( + udt_NumberVar *var, // variable to determine value for + unsigned pos) // array position +{ + char stringValue[200]; + long integerValue; + ub4 stringLength; + PyObject *result; + sword status; + + if (var->type == &vt_Integer || var->type == &vt_Boolean) { + status = OCINumberToInt(var->environment->errorHandle, &var->data[pos], + sizeof(long), OCI_NUMBER_SIGNED, (dvoid*) &integerValue); + if (Environment_CheckForError(var->environment, status, + "NumberVar_GetValue(): as integer") < 0) + return NULL; + if (var->type == &vt_Integer) + return PyInt_FromLong(integerValue); +#if (PY_VERSION_HEX >= 0x02030000) + return PyBool_FromLong(integerValue); +#endif + } + + if (var->type == &vt_NumberAsString || var->type == &vt_LongInteger) { + stringLength = sizeof(stringValue); + status = OCINumberToText(var->environment->errorHandle, + &var->data[pos], (unsigned char*) "TM9", 3, NULL, 0, + &stringLength, (unsigned char*) stringValue); + if (Environment_CheckForError(var->environment, status, + "NumberVar_GetValue(): as string") < 0) + return NULL; + if (var->type == &vt_NumberAsString) + return PyString_FromStringAndSize(stringValue, stringLength); + result = PyInt_FromString(stringValue, NULL, 10); + if (result || !PyErr_ExceptionMatches(PyExc_ValueError)) + return result; + PyErr_Clear(); + result = PyLong_FromString(stringValue, NULL, 10); + if (result || !PyErr_ExceptionMatches(PyExc_ValueError)) + return result; + PyErr_Clear(); + } + + return OracleNumberToPythonFloat(var->environment, &var->data[pos]); +} + + +#ifdef SQLT_BFLOAT +//----------------------------------------------------------------------------- +// NumberVar_GetValueAsDouble() +// Returns the value stored at the given array position as a float. +//----------------------------------------------------------------------------- +static PyObject *NumberVar_GetValueAsDouble( + udt_NumberVar *var, // variable to determine value for + unsigned pos) // array position +{ + double *doublePtr; + + doublePtr = (double*) &var->data[pos]; + return PyFloat_FromDouble(*doublePtr); +} +#endif + diff --git a/ObjectType.c b/ObjectType.c new file mode 100644 index 0000000..38be619 --- /dev/null +++ b/ObjectType.c @@ -0,0 +1,518 @@ +//----------------------------------------------------------------------------- +// ObjectType.c +// Defines the routines for handling Oracle type information. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// structures used for handling object types +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + udt_Environment *environment; + OCIType *tdo; + PyObject *schema; + PyObject *name; + PyObject *attributes; + PyObject *attributesByName; + OCITypeCode collectionTypeCode; + OCITypeCode elementTypeCode; + PyObject *elementType; + int isCollection; +} udt_ObjectType; + +typedef struct { + PyObject_HEAD + PyObject *name; + OCITypeCode typeCode; + udt_ObjectType *subType; +} udt_ObjectAttribute; + + +//----------------------------------------------------------------------------- +// Declaration of type variable functions. +//----------------------------------------------------------------------------- +static udt_ObjectType *ObjectType_New(udt_Connection*, OCIParam*); +static void ObjectType_Free(udt_ObjectType*); +static PyObject *ObjectType_Repr(udt_ObjectType*); +static udt_ObjectAttribute *ObjectAttribute_New(udt_Connection*, OCIParam*); +static void ObjectAttribute_Free(udt_ObjectAttribute*); +static PyObject *ObjectAttribute_Repr(udt_ObjectAttribute*); + + +//----------------------------------------------------------------------------- +// declaration of members for Python type "ObjectType" +//----------------------------------------------------------------------------- +static PyMemberDef g_ObjectTypeMembers[] = { + { "schema", T_OBJECT, offsetof(udt_ObjectType, schema), READONLY }, + { "name", T_OBJECT, offsetof(udt_ObjectType, name), READONLY }, + { "attributes", T_OBJECT, offsetof(udt_ObjectType, attributes), READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of members for Python type "ObjectAttribute" +//----------------------------------------------------------------------------- +static PyMemberDef g_ObjectAttributeMembers[] = { + { "name", T_OBJECT, offsetof(udt_ObjectAttribute, name), READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_ObjectTypeType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.ObjectType", // tp_name + sizeof(udt_ObjectType), // tp_basicsize + 0, // tp_itemsize + (destructor) ObjectType_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) ObjectType_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, // tp_flags + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + g_ObjectTypeMembers, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + 0, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0 // tp_bases +}; + + +static PyTypeObject g_ObjectAttributeType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.ObjectAttribute", // tp_name + sizeof(udt_ObjectAttribute), // tp_basicsize + 0, // tp_itemsize + (destructor) ObjectAttribute_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) ObjectAttribute_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, // tp_flags + 0, // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + g_ObjectAttributeMembers, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + 0, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0 // tp_bases +}; + + +//----------------------------------------------------------------------------- +// ObjectType_Describe() +// Describe the type and store information about it as needed. +//----------------------------------------------------------------------------- +static int ObjectType_Describe( + udt_ObjectType *self, // type to populate + udt_Connection *connection, // connection for type information + OCIDescribe *describeHandle) // describe handle +{ + OCIParam *topLevelParam, *attributeListParam, *attributeParam; + udt_ObjectAttribute *attribute; + OCIParam *collectionParam; + OCITypeCode typeCode; + ub2 numAttributes; + sword status; + int i; + + // describe the type + status = OCIDescribeAny(connection->handle, self->environment->errorHandle, + (dvoid*) self->tdo, 0, OCI_OTYPE_PTR, OCI_DEFAULT, OCI_PTYPE_TYPE, + describeHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): describe type") < 0) + return -1; + + // get top level parameter descriptor + status = OCIAttrGet(describeHandle, OCI_HTYPE_DESCRIBE, &topLevelParam, 0, + OCI_ATTR_PARAM, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get top level parameter descriptor") < 0) + return -1; + + // determine type of type + status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, &typeCode, 0, + OCI_ATTR_TYPECODE, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get type code") < 0) + return -1; + + // if a collection, need to determine the sub type + if (typeCode == OCI_TYPECODE_NAMEDCOLLECTION) { + self->isCollection = 1; + + // determine type of collection + status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, + &self->collectionTypeCode, 0, OCI_ATTR_COLLECTION_TYPECODE, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get collection type code") < 0) + return -1; + + // acquire collection parameter descriptor + status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, &collectionParam, + 0, OCI_ATTR_COLLECTION_ELEMENT, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get collection descriptor") < 0) + return -1; + + // determine type of element + status = OCIAttrGet(collectionParam, OCI_DTYPE_PARAM, + &self->elementTypeCode, 0, OCI_ATTR_TYPECODE, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get element type code") < 0) + return -1; + + // if element type is an object type get its type + if (self->elementTypeCode == OCI_TYPECODE_OBJECT) { + self->elementType = (PyObject*) + ObjectType_New(connection, collectionParam); + if (!self->elementType) + return -1; + } + + } + + // determine the number of attributes + status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, + (dvoid*) &numAttributes, 0, OCI_ATTR_NUM_TYPE_ATTRS, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get number of attributes") < 0) + return -1; + + // allocate the attribute list and dictionary + self->attributes = PyList_New(numAttributes); + if (!self->attributes) + return -1; + self->attributesByName = PyDict_New(); + if (!self->attributesByName) + return -1; + + // acquire the list parameter descriptor + status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, + (dvoid*) &attributeListParam, 0, OCI_ATTR_LIST_TYPE_ATTRS, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get list parameter descriptor") < 0) + return -1; + + // create attribute information for each attribute + for (i = 0; i < numAttributes; i++) { + status = OCIParamGet(attributeListParam, OCI_DTYPE_PARAM, + self->environment->errorHandle, (dvoid**) &attributeParam, + (ub4) i + 1); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Describe(): get attribute param descriptor") < 0) + return -1; + attribute = ObjectAttribute_New(connection, attributeParam); + if (!attribute) + return -1; + PyList_SET_ITEM(self->attributes, i, (PyObject*) attribute); + if (PyDict_SetItem(self->attributesByName, attribute->name, + (PyObject*) attribute) < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// ObjectType_Initialize() +// Initialize the object type with the information that is required. +//----------------------------------------------------------------------------- +static int ObjectType_Initialize( + udt_ObjectType *self, // type to initialize + udt_Connection *connection, // connection for type information + OCIParam *param) // parameter descriptor +{ + OCIDescribe *describeHandle; + OCIRef *tdoReference; + sword status; + char *name; + ub4 size; + + // determine the schema of the type + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &name, &size, + OCI_ATTR_SCHEMA_NAME, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): get schema name") < 0) + return -1; + self->schema = PyString_FromStringAndSize(name, size); + if (!self->schema) + return -1; + + // determine the name of the type + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &name, &size, + OCI_ATTR_TYPE_NAME, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): get name") < 0) + return -1; + self->name = PyString_FromStringAndSize(name, size); + if (!self->name) + return -1; + + // retrieve TDO of the parameter + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &tdoReference, 0, + OCI_ATTR_REF_TDO, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): get TDO reference") < 0) + return -1; + status = OCIObjectPin(self->environment->handle, + self->environment->errorHandle, tdoReference, NULL, OCI_PIN_ANY, + OCI_DURATION_SESSION, OCI_LOCK_NONE, (dvoid**) &self->tdo); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): pin TDO reference") < 0) + return -1; + + // acquire a describe handle + status = OCIHandleAlloc(self->environment->handle, + (dvoid**) &describeHandle, OCI_HTYPE_DESCRIBE, 0, 0); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): allocate describe handle") < 0) + return -1; + + // describe the type + if (ObjectType_Describe(self, connection, describeHandle) < 0) + return -1; + + // free the describe handle + status = OCIHandleFree(describeHandle, OCI_HTYPE_DESCRIBE); + if (Environment_CheckForError(self->environment, status, + "ObjectType_Initialize(): free describe handle") < 0) + return -1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// ObjectType_New() +// Allocate a new object type. +//----------------------------------------------------------------------------- +static udt_ObjectType *ObjectType_New( + udt_Connection *connection, // connection for type information + OCIParam *param) // parameter descriptor +{ + udt_ObjectType *self; + + self = PyObject_NEW(udt_ObjectType, &g_ObjectTypeType); + if (!self) + return NULL; + Py_INCREF(connection->environment); + self->environment = connection->environment; + self->tdo = NULL; + self->schema = NULL; + self->name = NULL; + self->attributes = NULL; + self->attributesByName = NULL; + self->elementType = NULL; + self->isCollection = 0; + if (ObjectType_Initialize(self, connection, param) < 0) { + Py_DECREF(self); + return NULL; + } + + return self; +} + + +//----------------------------------------------------------------------------- +// ObjectType_Free() +// Free the memory associated with an object type. +//----------------------------------------------------------------------------- +static void ObjectType_Free( + udt_ObjectType *self) // object type to free +{ + Py_DECREF(self->environment); + Py_XDECREF(self->schema); + Py_XDECREF(self->name); + Py_XDECREF(self->attributes); + Py_XDECREF(self->attributesByName); + Py_XDECREF(self->elementType); + if (self->tdo) + OCIObjectUnpin(self->environment->handle, + self->environment->errorHandle, self->tdo); + PyObject_DEL(self); +} + + +//----------------------------------------------------------------------------- +// ObjectType_Repr() +// Return a string representation of the object type. +//----------------------------------------------------------------------------- +static PyObject *ObjectType_Repr( + udt_ObjectType *self) // object type to return the string for +{ + PyObject *module, *name, *result; + + if (GetModuleAndName(self->ob_type, &module, &name) < 0) + return NULL; + result = PyString_FromFormat("<%s.%s %s.%s>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(self->schema), PyString_AS_STRING(self->name)); + Py_DECREF(module); + Py_DECREF(name); + return result; +} + + +//----------------------------------------------------------------------------- +// ObjectAttribute_Initialize() +// Initialize the new object attribute. +//----------------------------------------------------------------------------- +static int ObjectAttribute_Initialize( + udt_ObjectAttribute *self, // object attribute to initialize + udt_Connection *connection, // connection in use + OCIParam *param) // parameter descriptor +{ + sword status; + char *name; + ub4 size; + + // determine the name of the attribute + status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid*) &name, &size, + OCI_ATTR_NAME, connection->environment->errorHandle); + if (Environment_CheckForError(connection->environment, status, + "ObjectAttribute_Initialize(): get name") < 0) + return -1; + self->name = PyString_FromStringAndSize(name, size); + if (!self->name) + return -1; + + // determine the type of the attribute + status = OCIAttrGet(param, OCI_DTYPE_PARAM, (dvoid*) &self->typeCode, 0, + OCI_ATTR_TYPECODE, connection->environment->errorHandle); + if (Environment_CheckForError(connection->environment, status, + "ObjectAttribute_Initialize(): get type code") < 0) + return -1; + + // if the type of the attribute is object, recurse + switch (self->typeCode) { + case OCI_TYPECODE_NAMEDCOLLECTION: + case OCI_TYPECODE_OBJECT: + self->subType = ObjectType_New(connection, param); + if (!self->subType) + return -1; + break; + }; + + return 0; +} + + +//----------------------------------------------------------------------------- +// ObjectAttribute_New() +// Allocate a new object attribute. +//----------------------------------------------------------------------------- +static udt_ObjectAttribute *ObjectAttribute_New( + udt_Connection *connection, // connection information + OCIParam *param) // parameter descriptor +{ + udt_ObjectAttribute *self; + + self = PyObject_NEW(udt_ObjectAttribute, &g_ObjectAttributeType); + if (!self) + return NULL; + self->name = NULL; + self->subType = NULL; + if (ObjectAttribute_Initialize(self, connection, param) < 0) { + Py_DECREF(self); + return NULL; + } + + return self; +} + + +//----------------------------------------------------------------------------- +// ObjectAttribute_Free() +// Free the memory associated with an object attribute. +//----------------------------------------------------------------------------- +static void ObjectAttribute_Free( + udt_ObjectAttribute *self) // object attribute to free +{ + Py_XDECREF(self->name); + Py_XDECREF(self->subType); + PyObject_DEL(self); +} + + +//----------------------------------------------------------------------------- +// ObjectAttribute_Repr() +// Return a string representation of the object attribute. +//----------------------------------------------------------------------------- +static PyObject *ObjectAttribute_Repr( + udt_ObjectAttribute *self) // attribute to return the string for +{ + PyObject *module, *name, *result; + + if (GetModuleAndName(self->ob_type, &module, &name) < 0) + return NULL; + result = PyString_FromFormat("<%s.%s %s>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(self->name)); + Py_DECREF(module); + Py_DECREF(name); + return result; +} + diff --git a/ObjectVar.c b/ObjectVar.c new file mode 100644 index 0000000..2c7063f --- /dev/null +++ b/ObjectVar.c @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// ObjectVar.c +// Defines the routines for handling Oracle object variables. +//----------------------------------------------------------------------------- + +#include "ObjectType.c" +#include "ExternalObjectVar.c" + +//----------------------------------------------------------------------------- +// Object type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + dvoid **data; + dvoid **objectIndicator; + udt_Connection *connection; + udt_ObjectType *objectType; +} udt_ObjectVar; + +//----------------------------------------------------------------------------- +// Declaration of object variable functions. +//----------------------------------------------------------------------------- +static int ObjectVar_Initialize(udt_ObjectVar*, udt_Cursor*); +static void ObjectVar_Finalize(udt_ObjectVar*); +static PyObject *ObjectVar_GetAttr(udt_ObjectVar*, PyObject*); +static PyObject *ObjectVar_GetValue(udt_ObjectVar*, unsigned); +static int ObjectVar_PreDefine(udt_ObjectVar*, OCIParam*); +static int ObjectVar_PostDefine(udt_ObjectVar*); +static int ObjectVar_IsNull(udt_ObjectVar*, unsigned); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_ObjectVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.OBJECTVAR", // tp_name + sizeof(udt_ObjectVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) ObjectVar_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_Object = { + (InitializeProc) ObjectVar_Initialize, + (FinalizeProc) ObjectVar_Finalize, + (PreDefineProc) ObjectVar_PreDefine, + (PostDefineProc) ObjectVar_PostDefine, + (IsNullProc) ObjectVar_IsNull, + (SetValueProc) NULL, + (GetValueProc) ObjectVar_GetValue, + &g_ObjectVarType, // Python type + SQLT_NTY, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(dvoid*), // element length + 0, // is variable length + 0, // can be copied + 0 // can be in array +}; + + +//----------------------------------------------------------------------------- +// ObjectVar_Initialize() +// Initialize the variable. +//----------------------------------------------------------------------------- +static int ObjectVar_Initialize( + udt_ObjectVar *self, // variable to initialize + udt_Cursor *cursor) // cursor to use +{ + int i; + + Py_INCREF(cursor->connection); + self->connection = cursor->connection; + self->objectType = NULL; + self->objectIndicator = + PyMem_Malloc(self->allocatedElements * sizeof(dvoid*)); + if (!self->objectIndicator) { + PyErr_NoMemory(); + return -1; + } + for (i = 0; i < self->allocatedElements; i++) { + self->data[i] = NULL; + self->objectIndicator[i] = NULL; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// ObjectVar_Finalize() +// Prepare for variable destruction. +//----------------------------------------------------------------------------- +static void ObjectVar_Finalize( + udt_ObjectVar *self) // variable to free +{ + int i; + + for (i = 0; i < self->allocatedElements; i++) { + if (self->data[i]) + OCIObjectFree(self->environment->handle, + self->environment->errorHandle, self->data[i], + OCI_OBJECTFREE_FORCE); + } + Py_DECREF(self->connection); + Py_XDECREF(self->objectType); + if (self->objectIndicator) + PyMem_Free(self->objectIndicator); +} + + +//----------------------------------------------------------------------------- +// ObjectVar_GetAttr() +// Retrieve an attribute on the variable object. +//----------------------------------------------------------------------------- +static PyObject *ObjectVar_GetAttr( + udt_ObjectVar *self, // variable object + PyObject *nameObject) // name of attribute +{ + char *name; + + name = PyString_AS_STRING(nameObject); + if (name[0] == 't' && strcmp(name, "type") == 0) { + Py_INCREF(self->objectType); + return (PyObject*) self->objectType; + } + return Variable_GetAttr((udt_Variable*) self, nameObject); +} + + +//----------------------------------------------------------------------------- +// ObjectVar_PreDefine() +// Performs additional steps required for defining objects. +//----------------------------------------------------------------------------- +static int ObjectVar_PreDefine( + udt_ObjectVar *self, // variable to set up + OCIParam *param) // parameter being defined +{ + self->objectType = ObjectType_New(self->connection, param); + if (!self->objectType) + return -1; + return 0; +} + + +//----------------------------------------------------------------------------- +// ObjectVar_PostDefine() +// Performs additional steps required for defining objects. +//----------------------------------------------------------------------------- +static int ObjectVar_PostDefine( + udt_ObjectVar *self) // variable to set up +{ + sword status; + + status = OCIDefineObject(self->defineHandle, + self->environment->errorHandle, self->objectType->tdo, self->data, + 0, self->objectIndicator, 0); + return Environment_CheckForError(self->environment, status, + "ObjectVar_PostDefine(): define object"); +} + + +//----------------------------------------------------------------------------- +// ObjectVar_IsNull() +// Returns a boolean indicating if the variable is null or not. +//----------------------------------------------------------------------------- +static int ObjectVar_IsNull( + udt_ObjectVar *self, // variable to set up + unsigned pos) // position to check +{ + if (!self->objectIndicator[pos]) + return 1; + return (*((OCIInd*) self->objectIndicator[pos]) == OCI_IND_NULL); +} + + +//----------------------------------------------------------------------------- +// ObjectVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *ObjectVar_GetValue( + udt_ObjectVar *self, // variable to determine value for + unsigned pos) // array position +{ + PyObject *var; + + // only allowed to get the value once (for now) + if (!self->data[pos]) { + PyErr_SetString(g_ProgrammingErrorException, + "variable value can only be acquired once"); + return NULL; + } + + // for collections, return the list rather than the object + if (self->objectType->isCollection) + return ExternalObjectVar_ConvertCollection(self->environment, + self->data[pos], (PyObject*) self, self->objectType); + + // for objects, return a representation of the object + var = ExternalObjectVar_New((PyObject*) self, self->objectType, + self->data[pos], self->objectIndicator[pos], 1); + if (!var) + return NULL; + self->data[pos] = NULL; + self->objectIndicator[pos] = NULL; + return var; +} + diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..eac8258 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,11 @@ +Metadata-Version: 1.0 +Name: cx_Oracle +Version: 4.3.1 +Summary: Python interface to Oracle +Home-page: http://starship.python.net/crew/atuining +Author: Anthony Tuininga +Author-email: anthony.tuininga@gmail.com +License: See LICENSE.txt +Description: Python interface to Oracle conforming to the Python DB API 2.0 specification. + See http://www.python.org/topics/database/DatabaseAPI-2.0.html. +Platform: UNKNOWN diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e6297a7 --- /dev/null +++ b/README.txt @@ -0,0 +1,57 @@ +Open Source Python/Oracle Utility - cx_Oracle + +cx_Oracle is a Python extension module that allows access to Oracle and +conforms to the Python database API 2.0 specifications with a few exceptions. + +See http://www.python.org/topics/database/DatabaseAPI-2.0.html for more +information on the Python database API specification. + +For comments, contact Anthony Tuininga at anthony.tuininga@gmail.com or use the +mailing list at http://lists.sourceforge.net/lists/listinfo/cx-oracle-users + + +BINARY INSTALL: +Place the file cx_Oracle.pyd or cx_Oracle.so anywhere on your Python path. + + +SOURCE INSTALL: +This module has been built with Oracle 8.1.7, 9.2.0 and 10.1.0 on Linux, +Solaris, HP/UX, Tru64 Unix and Windows. It will likely build on other +platforms and other Oracle versions but I haven't tried them. Use the provided +setup.py to build and install the module which makes use of the distutils +module. Note that on Windows, I have used mingw32 (http://www.mingw.org) and +the module will not build with MSVC without modification. The commands +required to build and install the module are as follows: + + python setup.py build + python setup.py install + + +USAGE EXAMPLE: + +import cx_Oracle + +# connect via SQL*Net string or by each segment in a separate argument +#connection = cx_Oracle.connect("user/password@TNS") +connection = cx_Oracle.connect("user", "password", "TNS") + +cursor = connection.cursor() +cursor.arraysize = 50 +cursor.execute(""" + select Col1, Col2, Col3 + from SomeTable + where Col4 = :arg_1 + and Col5 between :arg_2 and :arg_3""", + arg_1 = "VALUE", + arg_2 = 5, + arg_3 = 15) +for column_1, column_2, column_3 in cursor.fetchall(): + print "Values:", column_1, column_2, column_3 + + +EXCEPTIONS: +The only exception to the DB API specification is the lack of a nextset() +method which is not supported by Oracle. + +Please see the included documentation for additional information. + diff --git a/SessionPool.c b/SessionPool.c new file mode 100644 index 0000000..ac918a8 --- /dev/null +++ b/SessionPool.c @@ -0,0 +1,483 @@ +//----------------------------------------------------------------------------- +// SessionPool.c +// Handles session pooling (only available in Oracle 9i and up). +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// structure for the Python type "SessionPool" +//----------------------------------------------------------------------------- +typedef struct { + PyObject_HEAD + OCISPool *handle; + ub4 minSessions; + ub4 maxSessions; + ub4 sessionIncrement; + ub4 cacheSize; + PyObject *name; + PyObject *username; + PyObject *password; + PyObject *dsn; + udt_Environment *environment; + PyTypeObject *connectionType; +} udt_SessionPool; + +//----------------------------------------------------------------------------- +// constants for the OCI attributes +//----------------------------------------------------------------------------- +static ub4 gc_OpenAttribute = OCI_ATTR_SPOOL_OPEN_COUNT; +static ub4 gc_BusyAttribute = OCI_ATTR_SPOOL_BUSY_COUNT; +static ub4 gc_TimeoutAttribute = OCI_ATTR_SPOOL_TIMEOUT; +static ub4 gc_GetModeAttribute = OCI_ATTR_SPOOL_GETMODE; + +//----------------------------------------------------------------------------- +// functions for the Python type "SessionPool" +//----------------------------------------------------------------------------- +static PyObject *SessionPool_New(PyTypeObject*, PyObject*, PyObject*); +static int SessionPool_Init(udt_SessionPool*, PyObject*, PyObject*); +static void SessionPool_Free(udt_SessionPool*); +static PyObject *SessionPool_Acquire(udt_SessionPool*, PyObject*); +static PyObject *SessionPool_Drop(udt_SessionPool*, PyObject*); +static PyObject *SessionPool_Release(udt_SessionPool*, PyObject*); +static PyObject *SessionPool_GetOCIAttr(udt_SessionPool*, ub4*); +static int SessionPool_SetOCIAttr(udt_SessionPool*, PyObject*, ub4*); + + +//----------------------------------------------------------------------------- +// declaration of methods for Python type "SessionPool" +//----------------------------------------------------------------------------- +static PyMethodDef g_SessionPoolMethods[] = { + { "acquire", (PyCFunction) SessionPool_Acquire, METH_NOARGS }, + { "drop", (PyCFunction) SessionPool_Drop, METH_VARARGS }, + { "release", (PyCFunction) SessionPool_Release, METH_VARARGS }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of members for Python type "SessionPool" +//----------------------------------------------------------------------------- +static PyMemberDef g_SessionPoolMembers[] = { + { "username", T_OBJECT, offsetof(udt_SessionPool, username), READONLY }, + { "password", T_OBJECT, offsetof(udt_SessionPool, password), READONLY }, + { "dsn", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY }, + { "tnsentry", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY }, + { "name", T_OBJECT, offsetof(udt_SessionPool, name), READONLY }, + { "max", T_INT, offsetof(udt_SessionPool, maxSessions), READONLY }, + { "min", T_INT, offsetof(udt_SessionPool, minSessions), READONLY }, + { "increment", T_INT, offsetof(udt_SessionPool, sessionIncrement), + READONLY }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of calculated members for Python type "SessionPool" +//----------------------------------------------------------------------------- +static PyGetSetDef g_SessionPoolCalcMembers[] = { + { "opened", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_OpenAttribute }, + { "busy", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_BusyAttribute }, + { "timeout", (getter) SessionPool_GetOCIAttr, + (setter) SessionPool_SetOCIAttr, 0, &gc_TimeoutAttribute }, + { "getmode", (getter) SessionPool_GetOCIAttr, + (setter) SessionPool_SetOCIAttr, 0, &gc_GetModeAttribute }, + { NULL } +}; + + +//----------------------------------------------------------------------------- +// declaration of Python type "SessionPool" +//----------------------------------------------------------------------------- +static PyTypeObject g_SessionPoolType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "OracleSessionPool", // tp_name + sizeof(udt_SessionPool), // tp_basicsize + 0, // tp_itemsize + (destructor) SessionPool_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // 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 + 0, // tp_iter + 0, // tp_iternext + g_SessionPoolMethods, // tp_methods + g_SessionPoolMembers, // tp_members + g_SessionPoolCalcMembers, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc) SessionPool_Init, // tp_init + 0, // tp_alloc + (newfunc) SessionPool_New, // tp_new + 0, // tp_free + 0, // tp_is_gc + 0 // tp_bases +}; + + +#include "Connection.c" + + +//----------------------------------------------------------------------------- +// SessionPool_New() +// Create a new session pool object. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_New( + PyTypeObject *type, // type object + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + udt_SessionPool *newObject; + + // create the object + newObject = (udt_SessionPool*) type->tp_alloc(type, 0); + if (!newObject) + return NULL; + newObject->environment = NULL; + + return (PyObject*) newObject; +} + + +//----------------------------------------------------------------------------- +// SessionPool_Init() +// Initialize the session pool object. +//----------------------------------------------------------------------------- +static int SessionPool_Init( + udt_SessionPool *self, // session pool object + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + unsigned usernameLength, passwordLength, dsnLength, poolNameLength; + const char *username, *password, *dsn, *poolName; + unsigned minSessions, maxSessions, sessionIncrement; + PyTypeObject *connectionType; + PyObject *threadedObj; + int threaded; + sword status; + ub1 getMode; + + // define keyword arguments + static char *keywordList[] = { "user", "password", "dsn", "min", "max", + "increment", "connectiontype", "threaded", "getmode", NULL }; + + // parse arguments and keywords + threaded = 0; + threadedObj = NULL; + connectionType = &g_ConnectionType; + getMode = OCI_SPOOL_ATTRVAL_NOWAIT; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "s#s#s#iii|OOb", + keywordList, &username, &usernameLength, &password, + &passwordLength, &dsn, &dsnLength, &minSessions, + &maxSessions, &sessionIncrement, &connectionType, &threadedObj, + &getMode)) + return -1; + if (!PyType_Check(connectionType)) { + PyErr_SetString(g_ProgrammingErrorException, + "connectiontype must be a type"); + return -1; + } + if (!PyType_IsSubtype(connectionType, &g_ConnectionType)) { + PyErr_SetString(g_ProgrammingErrorException, + "connectiontype must be a subclass of Connection"); + return -1; + } + if (threadedObj) { + threaded = PyObject_IsTrue(threadedObj); + if (threaded < 0) + return -1; + } + + // initialize the object's members + Py_INCREF(connectionType); + self->connectionType = connectionType; + self->minSessions = minSessions; + self->maxSessions = maxSessions; + self->sessionIncrement = sessionIncrement; + + // set up the environment + self->environment = Environment_New(threaded); + if (!self->environment) + return -1; + + // create the string for the username + self->username = PyString_FromStringAndSize(username, usernameLength); + if (!self->username) + return -1; + + // create the string for the password + self->password = PyString_FromStringAndSize(password, passwordLength); + if (!self->password) + return -1; + + // create the string for the TNS entry + self->dsn = PyString_FromStringAndSize(dsn, dsnLength); + if (!self->dsn) + return -1; + + // create the session pool handle + status = OCIHandleAlloc(self->environment->handle, (dvoid**) &self->handle, + OCI_HTYPE_SPOOL, 0, 0); + if (Environment_CheckForError(self->environment, status, + "SessionPool_New(): allocate handle") < 0) + return -1; + + // create the session pool + Py_BEGIN_ALLOW_THREADS + status = OCISessionPoolCreate(self->environment->handle, + self->environment->errorHandle, self->handle, + (OraText**) &poolName, &poolNameLength, (OraText*) dsn, dsnLength, + minSessions, maxSessions, sessionIncrement, (OraText*) username, + usernameLength, (OraText*) password, passwordLength, + OCI_SPC_HOMOGENEOUS | OCI_SPC_STMTCACHE); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(self->environment, status, + "SessionPool_New(): create pool") < 0) + return -1; + + // create the string for the pool name + self->name = PyString_FromStringAndSize(poolName, poolNameLength); + if (!self->name) + return -1; + + // set the mode on the pool + status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, (dvoid*) &getMode, 0, + OCI_ATTR_SPOOL_GETMODE, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "SessionPool_New(): set wait mode") < 0) + return -1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// SessionPool_Free() +// Deallocate the session pool. +//----------------------------------------------------------------------------- +static void SessionPool_Free( + udt_SessionPool *self) // session pool +{ + if (self->handle) { + OCISessionPoolDestroy(self->handle, self->environment->errorHandle, + OCI_SPD_FORCE); + OCIHandleFree(self->handle, OCI_HTYPE_SPOOL); + } + Py_XDECREF(self->name); + Py_XDECREF(self->environment); + Py_XDECREF(self->username); + Py_XDECREF(self->password); + Py_XDECREF(self->dsn); + self->ob_type->tp_free((PyObject*) self); +} + + +//----------------------------------------------------------------------------- +// SessionPool_IsConnected() +// Determines if the session pool object is connected to the database. If +// not, a Python exception is raised. +//----------------------------------------------------------------------------- +static int SessionPool_IsConnected( + udt_SessionPool *self) // session pool +{ + if (!self->handle) { + PyErr_SetString(g_InterfaceErrorException, "not connected"); + return -1; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// SessionPool_Acquire() +// Create a new connection within the session pool. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_Acquire( + udt_SessionPool *self, // session pool + PyObject *args) // arguments +{ + PyObject *createArgs, *createKeywordArgs, *result; + + // make sure session pool is connected + if (SessionPool_IsConnected(self) < 0) + return NULL; + + // create arguments + createArgs = PyTuple_New(0); + if (!createArgs) + return NULL; + createKeywordArgs = PyDict_New(); + if (!createKeywordArgs) { + Py_DECREF(createArgs); + return NULL; + } + if (PyDict_SetItemString(createKeywordArgs, "pool", + (PyObject*) self) < 0) { + Py_DECREF(createArgs); + Py_DECREF(createKeywordArgs); + return NULL; + } + + // create the connection object + result = PyObject_Call( (PyObject*) self->connectionType, createArgs, + createKeywordArgs); + Py_DECREF(createArgs); + Py_DECREF(createKeywordArgs); + + return result; +} + + +//----------------------------------------------------------------------------- +// SessionPool_InternalRelease() +// Internal method used to release a connection back to the pool in order to +// allow for the possibility of dropping the connection. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_InternalRelease( + udt_SessionPool *self, // session pool + PyObject *args, // arguments + ub4 mode) // OCI mode to use +{ + udt_Connection *connection; + sword status; + + // connection is expected + if (!PyArg_ParseTuple(args, "O!", &g_ConnectionType, &connection)) + return NULL; + + // make sure session pool is connected + if (SessionPool_IsConnected(self) < 0) + return NULL; + if (connection->sessionPool != self) { + PyErr_SetString(g_ProgrammingErrorException, + "connection not acquired with this session pool"); + return NULL; + } + + // release the connection + Py_BEGIN_ALLOW_THREADS + status = OCITransRollback(connection->handle, + connection->environment->errorHandle, OCI_DEFAULT); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(connection->environment, status, + "SessionPool_Release(): rollback") < 0) + return NULL; + Py_BEGIN_ALLOW_THREADS + status = OCISessionRelease(connection->handle, + connection->environment->errorHandle, NULL, 0, mode); + Py_END_ALLOW_THREADS + if (Environment_CheckForError(connection->environment, status, + "SessionPool_Release(): release session") < 0) + return NULL; + + // ensure that the connection behaves as closed + Py_DECREF(connection->sessionPool); + connection->sessionPool = NULL; + connection->handle = NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// SessionPool_Drop() +// Release a connection back to the session pool, dropping it so that a new +// connection will be created if needed. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_Drop( + udt_SessionPool *self, // session pool + PyObject *args) // arguments +{ + return SessionPool_InternalRelease(self, args, OCI_SESSRLS_DROPSESS); +} + + +//----------------------------------------------------------------------------- +// SessionPool_Release() +// Release a connection back to the session pool. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_Release( + udt_SessionPool *self, // session pool + PyObject *args) // arguments +{ + return SessionPool_InternalRelease(self, args, OCI_DEFAULT); +} + + +//----------------------------------------------------------------------------- +// SessionPool_GetOCIAttr() +// Return the value for the OCI attribute. +//----------------------------------------------------------------------------- +static PyObject *SessionPool_GetOCIAttr( + udt_SessionPool *self, // session pool + ub4 *attribute) // OCI attribute type +{ + sword status; + ub4 value; + + // make sure session pool is connected + if (SessionPool_IsConnected(self) < 0) + return NULL; + + // get the value from the OCI + status = OCIAttrGet(self->handle, OCI_HTYPE_SPOOL, &value, 0, *attribute, + self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "SessionPool_GetOCIAttr()") < 0) + return NULL; + if (*attribute == OCI_ATTR_SPOOL_GETMODE) + return PyInt_FromLong((ub1) value); + return PyInt_FromLong(value); +} + + +//----------------------------------------------------------------------------- +// SessionPool_SetOCIAttr() +// Set the value of the OCI attribute. +//----------------------------------------------------------------------------- +static int SessionPool_SetOCIAttr( + udt_SessionPool *self, // session pool + PyObject *value, // value to set + ub4 *attribute) // OCI attribute type +{ + ub4 ociValue; + sword status; + + // make sure session pool is connected + if (SessionPool_IsConnected(self) < 0) + return -1; + + // set the value in the OCI + if (!PyInt_Check(value)) { + PyErr_SetString(PyExc_TypeError, "value must be an integer"); + return -1; + } + ociValue = PyInt_AS_LONG(value); + status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, &ociValue, 0, + *attribute, self->environment->errorHandle); + if (Environment_CheckForError(self->environment, status, + "SessionPool_SetOCIAttr()") < 0) + return -1; + return 0; +} + diff --git a/StringVar.c b/StringVar.c new file mode 100644 index 0000000..53d7fcf --- /dev/null +++ b/StringVar.c @@ -0,0 +1,323 @@ +//----------------------------------------------------------------------------- +// StringVar.c +// Defines the routines specific to the string type. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// String type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + char *data; + ub1 charsetForm; + ub2 charsetId; +} udt_StringVar; + + +//----------------------------------------------------------------------------- +// Declaration of string variable functions. +//----------------------------------------------------------------------------- +static int StringVar_Initialize(udt_StringVar*, udt_Cursor*); +static int StringVar_PreDefine(udt_StringVar*, OCIParam*); +static int StringVar_SetValue(udt_StringVar*, unsigned, PyObject*); +static PyObject *StringVar_GetValue(udt_StringVar*, unsigned); + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_StringVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.STRING", // tp_name + sizeof(udt_StringVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_FixedCharVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.FIXED_CHAR", // tp_name + sizeof(udt_StringVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_RowidVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.ROWID", // tp_name + sizeof(udt_StringVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +static PyTypeObject g_BinaryVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.BINARY", // tp_name + sizeof(udt_StringVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_String = { + (InitializeProc) StringVar_Initialize, + (FinalizeProc) NULL, + (PreDefineProc) StringVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) StringVar_SetValue, + (GetValueProc) StringVar_GetValue, + &g_StringVarType, // Python type + SQLT_CHR, // Oracle type + SQLCS_IMPLICIT, // charset form + MAX_STRING_CHARS, // element length (default) + 1, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_NationalCharString = { + (InitializeProc) StringVar_Initialize, + (FinalizeProc) NULL, + (PreDefineProc) StringVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) StringVar_SetValue, + (GetValueProc) StringVar_GetValue, + &g_StringVarType, // Python type + SQLT_CHR, // Oracle type + SQLCS_NCHAR, // charset form + MAX_STRING_CHARS, // element length (default) + 1, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_FixedChar = { + (InitializeProc) StringVar_Initialize, + (FinalizeProc) NULL, + (PreDefineProc) StringVar_PreDefine, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) StringVar_SetValue, + (GetValueProc) StringVar_GetValue, + &g_FixedCharVarType, // Python type + SQLT_AFC, // Oracle type + SQLCS_IMPLICIT, // charset form + 2000, // element length (default) + 1, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_Rowid = { + (InitializeProc) StringVar_Initialize, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) StringVar_SetValue, + (GetValueProc) StringVar_GetValue, + &g_RowidVarType, // Python type + SQLT_CHR, // Oracle type + SQLCS_IMPLICIT, // charset form + 18, // element length (default) + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +static udt_VariableType vt_Binary = { + (InitializeProc) StringVar_Initialize, + (FinalizeProc) NULL, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) StringVar_SetValue, + (GetValueProc) StringVar_GetValue, + &g_BinaryVarType, // Python type + SQLT_BIN, // Oracle type + SQLCS_IMPLICIT, // charset form + MAX_STRING_CHARS, // element length (default) + 1, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +//----------------------------------------------------------------------------- +// StringVar_Initialize() +// Initialize the variable. +//----------------------------------------------------------------------------- +static int StringVar_Initialize( + udt_StringVar *var, // variable to initialize + udt_Cursor *cursor) // cursor to use +{ + var->actualLength = (ub2*) PyMem_Malloc(var->allocatedElements * + sizeof(ub2)); + if (!var->actualLength) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// StringVar_PreDefine() +// Set the character set information when values are fetched from this +// variable. +//----------------------------------------------------------------------------- +static int StringVar_PreDefine( + udt_StringVar *var, // variable to initialize + OCIParam *param) // parameter handle +{ + sword status; + + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &var->charsetForm, + 0, OCI_ATTR_CHARSET_FORM, var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "StringVar_PreDefine(): getting charset form") < 0) + return -1; + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &var->charsetId, 0, + OCI_ATTR_CHARSET_ID, var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "StringVar_PreDefine(): getting charset id") < 0) + return -1; + + return 0; +} + + +//----------------------------------------------------------------------------- +// StringVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int StringVar_SetValue( + udt_StringVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + Py_ssize_t bufferSize; + const void *buffer; + + // get the buffer data and size for binding + if (PyString_Check(value)) { + buffer = PyString_AS_STRING(value); + bufferSize = PyString_GET_SIZE(value); + } else if (PyBuffer_Check(value)) { + if (PyObject_AsReadBuffer(value, &buffer, &bufferSize) < 0) + return -1; + } else { + PyErr_SetString(PyExc_TypeError, "expecting string or buffer data"); + return -1; + } + + // ensure that the buffer is not too large + if (bufferSize > var->maxLength) { + if (bufferSize > var->environment->maxStringBytes) { + PyErr_SetString(PyExc_ValueError, "string data too large"); + return -1; + } + if (Variable_Resize( (udt_Variable*) var, bufferSize) < 0) + return -1; + } + + // keep a copy of the string + var->actualLength[pos] = (ub2) bufferSize; + if (bufferSize) + memcpy(var->data + var->maxLength * pos, buffer, bufferSize); + + return 0; +} + + +//----------------------------------------------------------------------------- +// StringVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *StringVar_GetValue( + udt_StringVar *var, // variable to determine value for + unsigned pos) // array position +{ + return PyString_FromStringAndSize(var->data + pos * var->maxLength, + var->actualLength[pos]); +} + diff --git a/TimestampVar.c b/TimestampVar.c new file mode 100644 index 0000000..24139e9 --- /dev/null +++ b/TimestampVar.c @@ -0,0 +1,238 @@ +//----------------------------------------------------------------------------- +// TimestampVar.c +// Defines the routines for handling timestamp variables. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Timestamp type +//----------------------------------------------------------------------------- +typedef struct { + Variable_HEAD + OCIDateTime **data; +} udt_TimestampVar; + + +//----------------------------------------------------------------------------- +// Declaration of date/time variable functions. +//----------------------------------------------------------------------------- +static int TimestampVar_Initialize(udt_TimestampVar*, udt_Cursor*); +static void TimestampVar_Finalize(udt_TimestampVar*); +static int TimestampVar_SetValue(udt_TimestampVar*, unsigned, PyObject*); +static PyObject *TimestampVar_GetValue(udt_TimestampVar*, unsigned); + + +//----------------------------------------------------------------------------- +// Python type declarations +//----------------------------------------------------------------------------- +static PyTypeObject g_TimestampVarType = { + PyObject_HEAD_INIT(NULL) + 0, // ob_size + "cx_Oracle.TIMESTAMP", // tp_name + sizeof(udt_TimestampVar), // tp_basicsize + 0, // tp_itemsize + (destructor) Variable_Free, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc) Variable_Repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + (getattrofunc) Variable_GetAttr, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + 0 // tp_doc +}; + + +//----------------------------------------------------------------------------- +// variable type declarations +//----------------------------------------------------------------------------- +static udt_VariableType vt_Timestamp = { + (InitializeProc) TimestampVar_Initialize, + (FinalizeProc) TimestampVar_Finalize, + (PreDefineProc) NULL, + (PostDefineProc) NULL, + (IsNullProc) NULL, + (SetValueProc) TimestampVar_SetValue, + (GetValueProc) TimestampVar_GetValue, + &g_TimestampVarType, // Python type + SQLT_TIMESTAMP, // Oracle type + SQLCS_IMPLICIT, // charset form + sizeof(OCIDateTime*), // element length (default) + 0, // is variable length + 1, // can be copied + 1 // can be in array +}; + + +//----------------------------------------------------------------------------- +// TimestampVar_Initialize() +// Initialize the variable. +//----------------------------------------------------------------------------- +static int TimestampVar_Initialize( + udt_TimestampVar *var, // variable to initialize + udt_Cursor *cursor) // cursor variable associated with +{ + sword status; + ub4 i; + + // initialize the LOB locators + for (i = 0; i < var->allocatedElements; i++) { + status = OCIDescriptorAlloc(var->environment->handle, + (dvoid**) &var->data[i], OCI_DTYPE_TIMESTAMP, 0, 0); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_Initialize()") < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// TimestampVar_Finalize() +// Prepare for variable destruction. +//----------------------------------------------------------------------------- +static void TimestampVar_Finalize( + udt_TimestampVar *var) // variable to free +{ + ub4 i; + + for (i = 0; i < var->allocatedElements; i++) { + if (var->data[i]) + OCIDescriptorFree(var->data[i], OCI_DTYPE_TIMESTAMP); + } +} + + +#ifdef NATIVE_DATETIME +//----------------------------------------------------------------------------- +// TimestampVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int TimestampVar_SetValue( + udt_TimestampVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + sword status; + uword valid; + + // make sure a timestamp is being bound + if (!PyDateTime_Check(value)) { + PyErr_SetString(PyExc_TypeError, "expecting timestamp data"); + return -1; + } + + // store a copy of the value + status = OCIDateTimeConstruct(var->environment->handle, + var->environment->errorHandle, var->data[pos], + (sb2) PyDateTime_GET_YEAR(value), + PyDateTime_GET_MONTH(value), + PyDateTime_GET_DAY(value), + PyDateTime_DATE_GET_HOUR(value), + PyDateTime_DATE_GET_MINUTE(value), + PyDateTime_DATE_GET_SECOND(value), + PyDateTime_DATE_GET_MICROSECOND(value) * 1000, NULL, 0); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_SetValue(): create structure") < 0) + return -1; + status = OCIDateTimeCheck(var->environment->handle, + var->environment->errorHandle, var->data[pos], &valid); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_SetValue()") < 0) + return -1; + if (valid != 0) { + PyErr_SetString(g_DataErrorException, "invalid date"); + return -1; + } + + return 0; +} + +#else + +//----------------------------------------------------------------------------- +// TimestampVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int TimestampVar_SetValue( + udt_TimestampVar *var, // variable to set value for + unsigned pos, // array position to set + PyObject *value) // value to set +{ + udt_ExternalDateTimeVar *dateValue; + sword status; + uword valid; + + // make sure a date is being bound + if (value->ob_type != &g_ExternalDateTimeVarType) { + PyErr_SetString(PyExc_TypeError, "expecting date data"); + return -1; + } + + // store a copy of the value + dateValue = (udt_ExternalDateTimeVar*) value; + status = OCIDateTimeConstruct(var->environment->handle, + var->environment->errorHandle, var->data[pos], + dateValue->year, dateValue->month, dateValue->day, dateValue->hour, + dateValue->minute, dateValue->second, dateValue->fsecond * 1000, + NULL, 0); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_SetValue(): create structure") < 0) + return -1; + status = OCIDateTimeCheck(var->environment->handle, + var->environment->errorHandle, var->data[pos], &valid); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_SetValue()") < 0) + return -1; + if (valid != 0) { + PyErr_SetString(g_DataErrorException, "invalid date"); + return -1; + } + + return 0; +} +#endif + + +//----------------------------------------------------------------------------- +// TimestampVar_GetValue() +// Returns the value stored at the given array position. +//----------------------------------------------------------------------------- +static PyObject *TimestampVar_GetValue( + udt_TimestampVar *var, // variable to determine value for + unsigned pos) // array position +{ + ub1 hour, minute, second, month, day; + sword status; + ub4 fsecond; + sb2 year; + + status = OCIDateTimeGetDate(var->environment->handle, + var->environment->errorHandle, var->data[pos], &year, &month, + &day); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_GetValue(): date portion") < 0) + return NULL; + status = OCIDateTimeGetTime(var->environment->handle, + var->environment->errorHandle, var->data[pos], &hour, &minute, + &second, &fsecond); + if (Environment_CheckForError(var->environment, status, + "TimestampVar_GetValue(): time portion") < 0) + return NULL; +#ifdef NATIVE_DATETIME + return PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, + fsecond / 1000); +#else + return ExternalDateTimeVar_NewFromC(&g_ExternalDateTimeVarType, year, + month, day, hour, minute, second, fsecond / 1000); +#endif +} + diff --git a/Transforms.c b/Transforms.c new file mode 100644 index 0000000..61ec785 --- /dev/null +++ b/Transforms.c @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Transforms.c +// Provides methods for transforming Oracle data to Python objects or for +// setting Oracle data from Python objects. +//----------------------------------------------------------------------------- + +#ifdef NATIVE_DATETIME +static udt_VariableType vt_Date; +#endif + +//----------------------------------------------------------------------------- +// OracleDateToPythonDate() +// Return a Python date object given an Oracle date. +//----------------------------------------------------------------------------- +static PyObject *OracleDateToPythonDate( + udt_VariableType *varType, // variable type + OCIDate* value) // value to convert +{ + ub1 hour, minute, second, month, day; + sb2 year; + + OCIDateGetDate(value, &year, &month, &day); + OCIDateGetTime(value, &hour, &minute, &second); + +#ifdef NATIVE_DATETIME + if (varType == &vt_Date) + return PyDate_FromDate(year, month, day); + return PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, + 0); +#else + return ExternalDateTimeVar_NewFromC(&g_ExternalDateTimeVarType, year, + month, day, hour, minute, second, 0); +#endif +} + + +//----------------------------------------------------------------------------- +// OracleNumberToPythonFloat() +// Return a Python date object given an Oracle date. +//----------------------------------------------------------------------------- +static PyObject *OracleNumberToPythonFloat( + udt_Environment *environment, // environment + OCINumber* value) // value to convert +{ + double doubleValue; + sword status; + + status = OCINumberToReal(environment->errorHandle, + value, sizeof(double), (dvoid*) &doubleValue); + if (Environment_CheckForError(environment, status, + "OracleNumberToPythonFloat()") < 0) + return NULL; + return PyFloat_FromDouble(doubleValue); +} + diff --git a/Variable.c b/Variable.c new file mode 100644 index 0000000..07533d8 --- /dev/null +++ b/Variable.c @@ -0,0 +1,1204 @@ +//----------------------------------------------------------------------------- +// Variable.c +// Defines Python types for Oracle variables. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// define structure common to all variables +//----------------------------------------------------------------------------- +struct _udt_VariableType; +#define Variable_HEAD \ + PyObject_HEAD \ + OCIBind *bindHandle; \ + OCIDefine *defineHandle; \ + OCIStmt *boundCursorHandle; \ + PyObject *boundName; \ + ub4 boundPos; \ + udt_Environment *environment; \ + ub4 allocatedElements; \ + ub4 actualElements; \ + unsigned internalFetchNum; \ + int isArray; \ + int isAllocatedInternally; \ + sb2 *indicator; \ + ub2 *returnCode; \ + ub2 *actualLength; \ + ub4 maxLength; \ + struct _udt_VariableType *type; +typedef struct { + Variable_HEAD + void *data; +} udt_Variable; + + +//----------------------------------------------------------------------------- +// define function types for the common actions that take place on a variable +//----------------------------------------------------------------------------- +typedef int (*InitializeProc)(udt_Variable*, udt_Cursor*); +typedef void (*FinalizeProc)(udt_Variable*); +typedef int (*PreDefineProc)(udt_Variable*, OCIParam*); +typedef int (*PostDefineProc)(udt_Variable*); +typedef int (*IsNullProc)(udt_Variable*, unsigned); +typedef int (*SetValueProc)(udt_Variable*, unsigned, PyObject*); +typedef PyObject * (*GetValueProc)(udt_Variable*, unsigned); + + +//----------------------------------------------------------------------------- +// define structure for the common actions that take place on a variable +//----------------------------------------------------------------------------- +typedef struct _udt_VariableType { + InitializeProc initializeProc; + FinalizeProc finalizeProc; + PreDefineProc preDefineProc; + PostDefineProc postDefineProc; + IsNullProc isNullProc; + SetValueProc setValueProc; + GetValueProc getValueProc; + PyTypeObject *pythonType; + ub2 oracleType; + ub1 charsetForm; + ub4 elementLength; + int isVariableLength; + int canBeCopied; + int canBeInArray; +} udt_VariableType; + + +//----------------------------------------------------------------------------- +// Declaration of common variable functions. +//----------------------------------------------------------------------------- +static void Variable_Free(udt_Variable *); +static PyObject *Variable_GetAttr(udt_Variable *, PyObject *); +static PyObject *Variable_Repr(udt_Variable *); +static PyObject *Variable_ExternalCopy(udt_Variable *, PyObject *); +static PyObject *Variable_ExternalSetValue(udt_Variable *, PyObject *); +static PyObject *Variable_ExternalGetValue(udt_Variable *, PyObject *, + PyObject *); +static int Variable_InternalBind(udt_Variable *); +static int Variable_Resize(udt_Variable *, unsigned); + + +//----------------------------------------------------------------------------- +// declaration of methods for variables +//----------------------------------------------------------------------------- +static PyMethodDef g_VariableMethods[] = { + { "copy", (PyCFunction) Variable_ExternalCopy, METH_VARARGS }, + { "setvalue", (PyCFunction) Variable_ExternalSetValue, METH_VARARGS }, + { "getvalue", (PyCFunction) Variable_ExternalGetValue, + METH_VARARGS | METH_KEYWORDS }, + { NULL, NULL } +}; + + +#ifndef NATIVE_DATETIME +#include "ExternalDateTimeVar.c" +#endif +#include "Transforms.c" +#include "StringVar.c" +#include "LongVar.c" +#include "NumberVar.c" +#include "DateTimeVar.c" +#ifdef ORACLE_9I +#include "TimestampVar.c" +#endif +#include "LobVar.c" +#include "CursorVar.c" +#include "ObjectVar.c" + + +//----------------------------------------------------------------------------- +// Variable_New() +// Allocate a new variable. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_New( + udt_Cursor *cursor, // cursor to associate variable with + unsigned numElements, // number of elements to allocate + udt_VariableType *type, // variable type + ub4 elementLength) // used only for variable length types +{ + unsigned PY_LONG_LONG dataLength; + udt_Variable *var; + ub4 i; + + // attempt to allocate the object + var = PyObject_NEW(udt_Variable, type->pythonType); + if (!var) + return NULL; + + // perform basic initialization + Py_INCREF(cursor->connection->environment); + var->environment = cursor->connection->environment; + var->boundCursorHandle = NULL; + var->bindHandle = NULL; + var->defineHandle = NULL; + var->boundName = NULL; + var->boundPos = 0; + if (numElements < 1) + var->allocatedElements = 1; + else var->allocatedElements = numElements; + var->actualElements = 0; + var->internalFetchNum = 0; + var->isArray = 0; + var->isAllocatedInternally = 1; + var->type = type; + var->indicator = NULL; + var->data = NULL; + var->actualLength = NULL; + var->returnCode = NULL; + + // set the maximum length of the variable, ensure that a minimum of + // 2 bytes is allocated to ensure that the array size check works + var->maxLength = type->elementLength; + if (type->isVariableLength) { + if (elementLength < sizeof(ub2)) + elementLength = sizeof(ub2); + var->maxLength = + elementLength * cursor->environment->maxBytesPerCharacter; + } + + // allocate the indicator and data + dataLength = (unsigned PY_LONG_LONG) numElements * + (unsigned PY_LONG_LONG) var->maxLength; + if (dataLength > INT_MAX) { + PyErr_SetString(PyExc_ValueError, "array size too large"); + Py_DECREF(var); + return NULL; + } + var->indicator = PyMem_Malloc(numElements * sizeof(sb2)); + var->data = PyMem_Malloc((size_t) dataLength); + if (!var->indicator || !var->data) { + PyErr_NoMemory(); + Py_DECREF(var); + return NULL; + } + + // ensure that all variable values start out NULL + for (i = 0; i < numElements; i++) + var->indicator[i] = OCI_IND_NULL; + + // for variable length data, also allocate the return code + if (type->isVariableLength) { + var->returnCode = PyMem_Malloc(numElements * sizeof(ub2)); + if (!var->returnCode) { + PyErr_NoMemory(); + Py_DECREF(var); + return NULL; + } + } + + // perform extended initialization + if (var->type->initializeProc) { + if ((*var->type->initializeProc)(var, cursor) < 0) { + Py_DECREF(var); + return NULL; + } + } + + return var; +} + + +//----------------------------------------------------------------------------- +// Variable_Free() +// Free an existing variable. +//----------------------------------------------------------------------------- +static void Variable_Free( + udt_Variable *var) // variable to free +{ + if (var->isAllocatedInternally) { + if (var->type->finalizeProc) + (*var->type->finalizeProc)(var); + if (var->indicator) + PyMem_Free(var->indicator); + if (var->data) + PyMem_Free(var->data); + if (var->actualLength) + PyMem_Free(var->actualLength); + if (var->returnCode) + PyMem_Free(var->returnCode); + } + Py_DECREF(var->environment); + Py_XDECREF(var->boundName); + PyObject_DEL(var); +} + + +//----------------------------------------------------------------------------- +// Variable_Resize() +// Resize the variable. +//----------------------------------------------------------------------------- +static int Variable_Resize( + udt_Variable *var, // variable to resize + unsigned maxLength) // new length to use +{ + char *newData; + ub4 i; + + // allocate new memory for the larger size + newData = (char*) PyMem_Malloc(var->allocatedElements * maxLength); + if (!newData) { + PyErr_NoMemory(); + return -1; + } + + // copy the data from the original array to the new array + for (i = 0; i < var->allocatedElements; i++) + memcpy(newData + maxLength * i, + (void*) ( (char*) var->data + var->maxLength * i ), + var->maxLength); + PyMem_Free(var->data); + var->data = newData; + var->maxLength = maxLength; + + // force rebinding + if (var->boundName || var->boundPos > 0) { + if (Variable_InternalBind(var) < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Variable_Check() +// Returns a boolean indicating if the object is a variable. +//----------------------------------------------------------------------------- +static int Variable_Check( + PyObject *object) // Python object to check +{ + return (object->ob_type == &g_CursorVarType || + object->ob_type == &g_DateTimeVarType || + object->ob_type == &g_BFILEVarType || + object->ob_type == &g_BLOBVarType || + object->ob_type == &g_CLOBVarType || + object->ob_type == &g_NCLOBVarType || + object->ob_type == &g_LongStringVarType || + object->ob_type == &g_LongBinaryVarType || + object->ob_type == &g_NumberVarType || + object->ob_type == &g_StringVarType || + object->ob_type == &g_FixedCharVarType || + object->ob_type == &g_RowidVarType || + object->ob_type == &g_BinaryVarType +#ifdef ORACLE_9I + || object->ob_type == &g_TimestampVarType +#endif + ); +} + + +//----------------------------------------------------------------------------- +// Variable_TypeByPythonType() +// Return a variable type given a Python type object or NULL if the Python +// type does not have a corresponding variable type. +//----------------------------------------------------------------------------- +static udt_VariableType *Variable_TypeByPythonType( + udt_Cursor* cursor, // cursor variable created for + PyObject* type) // Python type +{ + if (type == (PyObject*) &g_StringVarType) + return &vt_String; + if (type == (PyObject*) &PyString_Type) + return &vt_String; + if (type == (PyObject*) &g_FixedCharVarType) + return &vt_FixedChar; + if (type == (PyObject*) &g_RowidVarType) + return &vt_Rowid; + if (type == (PyObject*) &g_BinaryVarType) + return &vt_Binary; + if (type == (PyObject*) &PyBuffer_Type) + return &vt_Binary; + if (type == (PyObject*) &g_LongStringVarType) + return &vt_LongString; + if (type == (PyObject*) &g_LongBinaryVarType) + return &vt_LongBinary; + if (type == (PyObject*) &g_BFILEVarType) + return &vt_BFILE; + if (type == (PyObject*) &g_BLOBVarType) + return &vt_BLOB; + if (type == (PyObject*) &g_CLOBVarType) + return &vt_CLOB; + if (type == (PyObject*) &g_NCLOBVarType) + return &vt_NCLOB; + if (type == (PyObject*) &g_NumberVarType) { + if (cursor->numbersAsStrings) + return &vt_NumberAsString; + return &vt_Float; + } + if (type == (PyObject*) &PyFloat_Type) + return &vt_Float; + if (type == (PyObject*) &PyInt_Type) + return &vt_Integer; + if (type == (PyObject*) &PyLong_Type) + return &vt_LongInteger; +#if (PY_VERSION_HEX >= 0x02030000) + if (type == (PyObject*) &PyBool_Type) + return &vt_Boolean; +#endif +#ifdef NATIVE_DATETIME + if (type == (PyObject*) PyDateTimeAPI->DateType) + return &vt_Date; + if (type == (PyObject*) PyDateTimeAPI->DateTimeType) + return &vt_DateTime; +#else + if (type == (PyObject*) &g_ExternalDateTimeVarType) + return &vt_DateTime; +#endif +#ifdef ORACLE_9I + if (type == (PyObject*) &g_TimestampVarType) + return &vt_Timestamp; +#endif + if (type == (PyObject*) &g_CursorVarType) + return &vt_Cursor; + + PyErr_SetString(g_NotSupportedErrorException, + "Variable_TypeByPythonType(): unhandled data type"); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Variable_TypeByValue() +// Return a variable type given a Python object or NULL if the Python +// object does not have a corresponding variable type. +//----------------------------------------------------------------------------- +static udt_VariableType *Variable_TypeByValue( + PyObject* value) // Python type +{ + PyObject *elementValue; + char buffer[200]; + int i, result; + + // handle scalars + if (value == Py_None) + return &vt_String; + if (PyString_Check(value)) + return &vt_String; + if (PyInt_Check(value)) + return &vt_Integer; + if (PyLong_Check(value)) + return &vt_LongInteger; + if (PyFloat_Check(value)) + return &vt_Float; + if (PyBuffer_Check(value)) + return &vt_Binary; +#if (PY_VERSION_HEX >= 0x02030000) + if (PyBool_Check(value)) + return &vt_Boolean; +#endif +#ifdef NATIVE_DATETIME + if (PyDateTime_Check(value)) + return &vt_DateTime; + if (PyDate_Check(value)) + return &vt_DateTime; +#else + if (value->ob_type == &g_ExternalDateTimeVarType) + return &vt_DateTime; +#endif + result = PyObject_IsInstance(value, (PyObject*) &g_CursorType); + if (result < 0) + return NULL; + if (result) + return &vt_Cursor; + if (value->ob_type == g_DateTimeType) + return &vt_DateTime; + if (value->ob_type == g_DecimalType) + return &vt_NumberAsString; + + // handle arrays + if (PyList_Check(value)) { + elementValue = Py_None; + for (i = 0; i < PyList_GET_SIZE(value); i++) { + elementValue = PyList_GET_ITEM(value, i); + if (elementValue != Py_None) + break; + } + return Variable_TypeByValue(elementValue); + } + + sprintf(buffer, "Variable_TypeByValue(): unhandled data type %.*s", 150, + value->ob_type->tp_name); + PyErr_SetString(g_NotSupportedErrorException, buffer); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Variable_TypeByOracleDataType() +// Return a variable type given an Oracle data type or NULL if the Oracle +// data type does not have a corresponding variable type. +//----------------------------------------------------------------------------- +static udt_VariableType *Variable_TypeByOracleDataType ( + ub2 oracleDataType, // Oracle data type + ub1 charsetForm) // character set form +{ + char buffer[100]; + + switch(oracleDataType) { + case SQLT_LNG: + return &vt_LongString; + case SQLT_AFC: + return &vt_FixedChar; + case SQLT_CHR: + if (charsetForm == SQLCS_NCHAR) + return &vt_NationalCharString; + return &vt_String; + case SQLT_RDD: + return &vt_Rowid; + case SQLT_BIN: + return &vt_Binary; + case SQLT_LBI: + return &vt_LongBinary; +#ifdef SQLT_BFLOAT + case SQLT_BFLOAT: + case SQLT_IBFLOAT: + case SQLT_BDOUBLE: + case SQLT_IBDOUBLE: + return &vt_NativeFloat; +#endif + case SQLT_NUM: + case SQLT_VNU: + return &vt_Float; + case SQLT_DAT: + case SQLT_ODT: + return &vt_DateTime; +#ifdef ORACLE_9I + case SQLT_DATE: + case SQLT_TIMESTAMP: + case SQLT_TIMESTAMP_TZ: + case SQLT_TIMESTAMP_LTZ: + return &vt_Timestamp; +#endif + case SQLT_CLOB: + if (charsetForm == SQLCS_NCHAR) + return &vt_NCLOB; + return &vt_CLOB; + case SQLT_BLOB: + return &vt_BLOB; + case SQLT_BFILE: + return &vt_BFILE; + case SQLT_RSET: + return &vt_Cursor; + case SQLT_NTY: + return &vt_Object; + } + + sprintf(buffer, "Variable_TypeByOracleDataType: unhandled data type %d", + oracleDataType); + PyErr_SetString(g_NotSupportedErrorException, buffer); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Variable_MakeArray() +// Make the variable an array, ensuring that the type supports arrays. +//----------------------------------------------------------------------------- +static int Variable_MakeArray( + udt_Variable *var) // variable to make an array +{ + if (!var->type->canBeInArray) { + PyErr_SetString(g_NotSupportedErrorException, + "Variable_MakeArray(): type does not support arrays"); + return -1; + } + var->isArray = 1; + return 0; +} + + +//----------------------------------------------------------------------------- +// Variable_NewByValue() +// Allocate a new variable by looking at the type of the data. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_NewByValue( + udt_Cursor *cursor, // cursor to associate variable with + PyObject *value, // Python value to associate + unsigned numElements) // number of elements to allocate +{ + udt_VariableType *varType; + Py_ssize_t bufferSize; + const void *buffer; + udt_Variable *var; + ub4 size = 0; + + varType = Variable_TypeByValue(value); + if (!varType) + return NULL; + if (value == Py_None) + size = 1; + else if (PyString_Check(value)) { + size = PyString_GET_SIZE(value); + if (size > cursor->environment->maxStringBytes) + varType = &vt_LongString; + } else if (PyBuffer_Check(value)) { + if (PyObject_AsReadBuffer(value, &buffer, &bufferSize) < 0) + return NULL; + size = (ub4) bufferSize; + if (size > cursor->environment->maxStringBytes) + varType = &vt_LongBinary; + } + if (PyList_Check(value)) { + numElements = PyList_GET_SIZE(value); + size = varType->elementLength; + } + var = Variable_New(cursor, numElements, varType, size); + if (!var) + return NULL; + if (PyList_Check(value)) { + if (Variable_MakeArray(var) < 0) { + Py_DECREF(var); + return NULL; + } + } + + return var; +} + + +//----------------------------------------------------------------------------- +// Variable_NewArrayByType() +// Allocate a new PL/SQL array by looking at the Python data type. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_NewArrayByType( + udt_Cursor *cursor, // cursor to bind variable to + PyObject *value) // value to bind +{ + PyObject *type, *numElements; + udt_VariableType *varType; + udt_Variable *var; + + if (PyList_GET_SIZE(value) != 2) { + PyErr_SetString(g_ProgrammingErrorException, + "expecting an array of two elements [type, numelems]"); + return NULL; + } + + type = PyList_GET_ITEM(value, 0); + numElements = PyList_GET_ITEM(value, 1); + if (!PyInt_Check(numElements)) { + PyErr_SetString(g_ProgrammingErrorException, + "number of elements must be an integer"); + return NULL; + } + + varType = Variable_TypeByPythonType(cursor, type); + if (!varType) + return NULL; + + var = Variable_New(cursor, PyInt_AS_LONG(numElements), varType, + varType->elementLength); + if (!var) + return NULL; + if (Variable_MakeArray(var) < 0) { + Py_DECREF(var); + return NULL; + } + + return var; +} + + +//----------------------------------------------------------------------------- +// Variable_NewByType() +// Allocate a new variable by looking at the Python data type. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_NewByType( + udt_Cursor *cursor, // cursor to associate variable with + PyObject *value, // Python data type to associate + unsigned numElements) // number of elements to allocate +{ + udt_VariableType *varType; + int maxLength; + + // passing an integer is assumed to be a string + if (PyInt_Check(value)) { + maxLength = PyInt_AS_LONG(value); + if (maxLength > MAX_STRING_CHARS) + varType = &vt_LongString; + else varType = &vt_String; + return Variable_New(cursor, numElements, varType, maxLength); + } + + // passing an array of two elements to define an array + if (PyList_Check(value)) + return Variable_NewArrayByType(cursor, value); + + // handle directly bound variables + if (Variable_Check(value)) { + Py_INCREF(value); + return (udt_Variable*) value; + } + + // everything else ought to be a Python type + varType = Variable_TypeByPythonType(cursor, value); + if (!varType) + return NULL; + return Variable_New(cursor, numElements, varType, varType->elementLength); +} + + +//----------------------------------------------------------------------------- +// Variable_DefineHelper() +// Helper routine for Variable_Define() used so that constant calls to +// OCIDescriptorFree() is not necessary. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_DefineHelper( + udt_Cursor *cursor, // cursor in use + OCIParam *param, // parameter descriptor + unsigned position, // position in define list + unsigned numElements) // number of elements to create +{ + ub2 dataType, lengthFromOracle; + udt_VariableType *varType; + udt_Variable *var; + ub1 charsetForm; + ub4 maxLength; + sword status; + + // retrieve datatype of the parameter + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &dataType, 0, + OCI_ATTR_DATA_TYPE, cursor->environment->errorHandle); + if (Environment_CheckForError(cursor->environment, status, + "Variable_Define(): data type") < 0) + return NULL; + + // retrieve character set form of the parameter + if (dataType != SQLT_CHR && dataType != SQLT_CLOB) { + charsetForm = SQLCS_IMPLICIT; + } else { + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &charsetForm, + 0, OCI_ATTR_CHARSET_FORM, cursor->environment->errorHandle); + if (Environment_CheckForError(cursor->environment, status, + "Variable_Define(): charset form") < 0) + return NULL; + } + + // determine data type + varType = Variable_TypeByOracleDataType(dataType, charsetForm); + if (!varType) + return NULL; + if (cursor->numbersAsStrings && varType == &vt_Float) + varType = &vt_NumberAsString; + + // retrieve size of the parameter + maxLength = varType->elementLength; + if (varType->isVariableLength) { + + // determine the maximum length from Oracle + status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, + (dvoid*) &lengthFromOracle, 0, OCI_ATTR_DATA_SIZE, + cursor->environment->errorHandle); + if (Environment_CheckForError(cursor->environment, status, + "Variable_Define(): data size") < 0) + return NULL; + + // use the length from Oracle directly if available + if (lengthFromOracle) + maxLength = lengthFromOracle; + + // otherwise, use the value set with the setoutputsize() parameter but + // since long strings have the length embedded in them, increase the + // size by the size of an integer to make things work as expected + else if (cursor->outputSize >= 0) { + if (cursor->outputSizeColumn < 0 || + (int) position == cursor->outputSizeColumn) + maxLength = cursor->outputSize + sizeof(ub4); + } + } + + // create a variable of the correct type + var = Variable_New(cursor, numElements, varType, maxLength); + if (!var) + return NULL; + + // call the procedure to set values prior to define + if (var->type->preDefineProc) { + if ((*var->type->preDefineProc)(var, param) < 0) { + Py_DECREF(var); + return NULL; + } + } + + // perform the define + status = OCIDefineByPos(cursor->handle, &var->defineHandle, + var->environment->errorHandle, position, var->data, + var->maxLength, var->type->oracleType, var->indicator, + var->actualLength, var->returnCode, OCI_DEFAULT); + if (Environment_CheckForError(var->environment, status, + "Variable_Define(): define") < 0) { + Py_DECREF(var); + return NULL; + } + + // call the procedure to set values after define + if (var->type->postDefineProc) { + if ((*var->type->postDefineProc)(var) < 0) { + Py_DECREF(var); + return NULL; + } + } + + return var; +} + + +//----------------------------------------------------------------------------- +// Variable_Define() +// Allocate a variable and define it for the given statement. +//----------------------------------------------------------------------------- +static udt_Variable *Variable_Define( + udt_Cursor *cursor, // cursor to define for + unsigned numElements, // number of elements to create + unsigned position) // position to define +{ + udt_Variable *var; + OCIParam *param; + sword status; + + // retrieve parameter descriptor + status = OCIParamGet(cursor->handle, OCI_HTYPE_STMT, + cursor->environment->errorHandle, (void**) ¶m, position); + if (Environment_CheckForError(cursor->environment, status, + "Variable_Define(): parameter") < 0) + return NULL; + + // call the helper to do the actual work + var = Variable_DefineHelper(cursor, param, position, numElements); + OCIDescriptorFree(param, OCI_DTYPE_PARAM); + return var; +} + + +//----------------------------------------------------------------------------- +// Variable_InternalBind() +// Allocate a variable and bind it to the given statement. +//----------------------------------------------------------------------------- +static int Variable_InternalBind( + udt_Variable *var) // variable to bind +{ + sword status; + + // perform the bind + if (var->boundName) { + if (var->isArray) { + status = OCIBindByName(var->boundCursorHandle, &var->bindHandle, + var->environment->errorHandle, + (unsigned char*) PyString_AS_STRING(var->boundName), + PyString_GET_SIZE(var->boundName), var->data, + var->maxLength, var->type->oracleType, var->indicator, + var->actualLength, var->returnCode, var->allocatedElements, + &var->actualElements, OCI_DEFAULT); + } else { + status = OCIBindByName(var->boundCursorHandle, &var->bindHandle, + var->environment->errorHandle, + (unsigned char*) PyString_AS_STRING(var->boundName), + PyString_GET_SIZE(var->boundName), var->data, + var->maxLength, var->type->oracleType, var->indicator, + var->actualLength, var->returnCode, 0, 0, OCI_DEFAULT); + } + } else { + if (var->isArray) { + status = OCIBindByPos(var->boundCursorHandle, &var->bindHandle, + var->environment->errorHandle, var->boundPos, var->data, + var->maxLength, var->type->oracleType, var->indicator, + var->actualLength, var->returnCode, var->allocatedElements, + &var->actualElements, OCI_DEFAULT); + } else { + status = OCIBindByPos(var->boundCursorHandle, &var->bindHandle, + var->environment->errorHandle, var->boundPos, var->data, + var->maxLength, var->type->oracleType, var->indicator, + var->actualLength, var->returnCode, 0, 0, OCI_DEFAULT); + } + } + if (Environment_CheckForError(var->environment, status, + "Variable_InternalBind()") < 0) + return -1; + + // set the charset form if applicable + if (var->type->charsetForm != SQLCS_IMPLICIT) { + status = OCIAttrSet(var->bindHandle, OCI_HTYPE_BIND, + (dvoid*) &var->type->charsetForm, 0, OCI_ATTR_CHARSET_FORM, + var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "Variable_InternalBind(): set charset form") < 0) + return -1; + } + + // set the max data size for strings + if ((var->type == &vt_String || var->type == &vt_FixedChar) + && var->maxLength > var->type->elementLength) { + status = OCIAttrSet(var->bindHandle, OCI_HTYPE_BIND, + (dvoid*) &var->type->elementLength, 0, OCI_ATTR_MAXDATA_SIZE, + var->environment->errorHandle); + if (Environment_CheckForError(var->environment, status, + "Variable_InternalBind(): set max data size") < 0) + return -1; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Variable_Bind() +// Allocate a variable and bind it to the given statement. +//----------------------------------------------------------------------------- +static int Variable_Bind( + udt_Variable *var, // variable to bind + udt_Cursor *cursor, // cursor to bind to + PyObject *name, // name to bind to + ub4 pos) // position to bind to +{ + // nothing to do if already bound + if (var->bindHandle && name == var->boundName && pos == var->boundPos) + return 0; + + // set the instance variables specific for binding + Py_XDECREF(var->boundName); + Py_XINCREF(name); + var->boundName = name; + var->boundPos = pos; + var->boundCursorHandle = cursor->handle; + + // perform the bind + return Variable_InternalBind(var); +} + + +//----------------------------------------------------------------------------- +// Variable_VerifyFetch() +// Verifies that truncation or other problems did not take place on retrieve. +//----------------------------------------------------------------------------- +static int Variable_VerifyFetch( + udt_Variable *var, // variable to check fetch for + unsigned arrayPos) // array position +{ + if (var->type->isVariableLength) { + if (var->returnCode[arrayPos] != 0) { + char buffer[100]; + sprintf(buffer, "column at array pos %d fetched with error: %d", + arrayPos, var->returnCode[arrayPos]); + PyErr_SetString(g_DatabaseErrorException, buffer); + return -1; + } + } + return 0; +} + + +//----------------------------------------------------------------------------- +// Variable_GetSingleValue() +// Return the value of the variable at the given position. +//----------------------------------------------------------------------------- +static PyObject *Variable_GetSingleValue( + udt_Variable *var, // variable to get the value for + unsigned arrayPos) // array position +{ + int isNull; + + // ensure we do not exceed the number of allocated elements + if (arrayPos >= var->allocatedElements) { + PyErr_SetString(PyExc_IndexError, + "Variable_GetSingleValue: array size exceeded"); + return NULL; + } + + // check for a NULL value + if (var->type->isNullProc) + isNull = (*var->type->isNullProc)(var, arrayPos); + else isNull = (var->indicator[arrayPos] == OCI_IND_NULL); + if (isNull) { + Py_INCREF(Py_None); + return Py_None; + } + + // check for truncation or other problems on retrieve + if (Variable_VerifyFetch(var, arrayPos) < 0) + return NULL; + + return (*var->type->getValueProc)(var, arrayPos); +} + + +//----------------------------------------------------------------------------- +// Variable_GetArrayValue() +// Return the value of the variable as an array. +//----------------------------------------------------------------------------- +static PyObject *Variable_GetArrayValue( + udt_Variable *var, // variable to get the value for + ub4 numElements) // number of elements to include +{ + PyObject *value, *singleValue; + ub4 i; + + value = PyList_New(numElements); + if (!value) + return NULL; + + for (i = 0; i < numElements; i++) { + singleValue = Variable_GetSingleValue(var, i); + if (!singleValue) { + Py_DECREF(value); + return NULL; + } + PyList_SET_ITEM(value, i, singleValue); + } + + return value; +} + + +//----------------------------------------------------------------------------- +// Variable_GetValue() +// Return the value of the variable. +//----------------------------------------------------------------------------- +static PyObject *Variable_GetValue( + udt_Variable *var, // variable to get the value for + unsigned arrayPos) // array position +{ + if (var->isArray) + return Variable_GetArrayValue(var, var->actualElements); + return Variable_GetSingleValue(var, arrayPos); +} + + +//----------------------------------------------------------------------------- +// Variable_SetSingleValue() +// Set a single value in the variable. +//----------------------------------------------------------------------------- +static int Variable_SetSingleValue( + udt_Variable *var, // variable to set value for + unsigned arrayPos, // array position + PyObject *value) // value to set +{ + // ensure we do not exceed the number of allocated elements + if (arrayPos >= var->allocatedElements) { + PyErr_SetString(PyExc_IndexError, + "Variable_SetSingleValue: array size exceeded"); + return -1; + } + + // check for a NULL value + if (value == Py_None) { + var->indicator[arrayPos] = OCI_IND_NULL; + return 0; + } + + var->indicator[arrayPos] = OCI_IND_NOTNULL; + if (var->type->isVariableLength) + var->returnCode[arrayPos] = 0; + return (*var->type->setValueProc)(var, arrayPos, value); +} + + +//----------------------------------------------------------------------------- +// Variable_SetArrayValue() +// Set all of the array values for the variable. +//----------------------------------------------------------------------------- +static int Variable_SetArrayValue( + udt_Variable *var, // variable to set value for + PyObject *value) // value to set +{ + unsigned numElements; + ub4 i; + + // ensure we have an array to set + if (!PyList_Check(value)) { + PyErr_SetString(PyExc_TypeError, "expecting array data"); + return -1; + } + + // ensure we haven't exceeded the number of allocated elements + numElements = PyList_GET_SIZE(value); + if (numElements > var->allocatedElements) { + PyErr_SetString(PyExc_IndexError, + "Variable_SetArrayValue: array size exceeded"); + return -1; + } + + // set all of the values + var->actualElements = numElements; + for (i = 0; i < var->actualElements; i++) { + if (Variable_SetSingleValue(var, i, PyList_GET_ITEM(value, i)) < 0) + return -1; + } + return 0; +} + + +//----------------------------------------------------------------------------- +// Variable_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int Variable_SetValue( + udt_Variable *var, // variable to set + unsigned arrayPos, // array position + PyObject *value) // value to set +{ + if (var->isArray) + return Variable_SetArrayValue(var, value); + return Variable_SetSingleValue(var, arrayPos, value); +} + + +//----------------------------------------------------------------------------- +// Variable_ExternalCopy() +// Copy the contents of the source variable to the destination variable. +//----------------------------------------------------------------------------- +static PyObject *Variable_ExternalCopy( + udt_Variable *targetVar, // variable to set + PyObject *args) // arguments +{ + unsigned sourcePos, targetPos; + udt_Variable *sourceVar; + + // parse arguments; verify that copy is possible + if (!PyArg_ParseTuple(args, "Oii", &sourceVar, &sourcePos, &targetPos)) + return NULL; + if (targetVar->ob_type != sourceVar->ob_type) { + PyErr_SetString(g_ProgrammingErrorException, + "source and target variable type must match"); + return NULL; + } + if (!sourceVar->type->canBeCopied) { + PyErr_SetString(g_ProgrammingErrorException, + "variable does not support copying"); + return NULL; + } + + // ensure array positions are not violated + if (sourcePos >= sourceVar->allocatedElements) { + PyErr_SetString(PyExc_IndexError, + "Variable_ExternalCopy: source array size exceeded"); + return NULL; + } + if (targetPos >= targetVar->allocatedElements) { + PyErr_SetString(PyExc_IndexError, + "Variable_ExternalCopy: target array size exceeded"); + return NULL; + } + + // ensure target can support amount data from the source + if (targetVar->maxLength < sourceVar->maxLength) { + PyErr_SetString(g_ProgrammingErrorException, + "target variable has insufficient space to copy source data"); + return NULL; + } + + // handle null case directly + if (sourceVar->indicator[sourcePos] == OCI_IND_NULL) + targetVar->indicator[targetPos] = OCI_IND_NULL; + + // otherwise, copy data + else { + targetVar->indicator[targetPos] = OCI_IND_NOTNULL; + if (Variable_VerifyFetch(sourceVar, sourcePos) < 0) + return NULL; + if (targetVar->actualLength) + targetVar->actualLength[targetPos] = + sourceVar->actualLength[sourcePos]; + memcpy( (char*) targetVar->data + targetPos * targetVar->maxLength, + (char*) sourceVar->data + sourcePos * sourceVar->maxLength, + sourceVar->maxLength); + } + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Variable_ExternalSetValue() +// Set the value of the variable at the given position. +//----------------------------------------------------------------------------- +static PyObject *Variable_ExternalSetValue( + udt_Variable *var, // variable to set + PyObject *args) // arguments +{ + PyObject *value; + unsigned pos; + + if (!PyArg_ParseTuple(args, "iO", &pos, &value)) + return NULL; + if (Variable_SetValue(var, pos, value) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +//----------------------------------------------------------------------------- +// Variable_ExternalGetValue() +// Return the value of the variable at the given position. +//----------------------------------------------------------------------------- +static PyObject *Variable_ExternalGetValue( + udt_Variable *var, // variable to set + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments +{ + static char *keywordList[] = { "pos", NULL }; + unsigned pos = 0; + + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList, + &pos)) + return NULL; + return Variable_GetValue(var, pos); +} + + +//----------------------------------------------------------------------------- +// Variable_GetAttr() +// Retrieve an attribute on the variable object. +//----------------------------------------------------------------------------- +static PyObject *Variable_GetAttr( + udt_Variable *var, // variable object + PyObject *nameObject) // name of attribute +{ + char *name; + + name = PyString_AS_STRING(nameObject); + if (name[0] == 'm' && strcmp(name, "maxlength") == 0) + return PyInt_FromLong(var->maxLength); + if (name[0] == 'a' && strcmp(name, "allocelems") == 0) + return PyInt_FromLong(var->allocatedElements); + + return Py_FindMethod(g_VariableMethods, (PyObject*) var, + PyString_AS_STRING(nameObject)); +} + + +//----------------------------------------------------------------------------- +// Variable_Repr() +// Return a string representation of the variable. +//----------------------------------------------------------------------------- +static PyObject *Variable_Repr( + udt_Variable *var) // variable to return the string for +{ + PyObject *valueRepr, *value, *module, *name, *result; + + if (var->isArray) + value = Variable_GetArrayValue(var, var->actualElements); + else if (var->allocatedElements == 1) + value = Variable_GetSingleValue(var, 0); + else value = Variable_GetArrayValue(var, var->allocatedElements); + if (!value) + return NULL; + valueRepr = PyObject_Repr(value); + Py_DECREF(value); + if (!valueRepr) + return NULL; + if (GetModuleAndName(var->ob_type, &module, &name) < 0) { + Py_DECREF(valueRepr); + return NULL; + } + result = PyString_FromFormat("<%s.%s with value %s>", + PyString_AS_STRING(module), PyString_AS_STRING(name), + PyString_AS_STRING(valueRepr)); + Py_DECREF(valueRepr); + Py_DECREF(module); + Py_DECREF(name); + return result; +} + diff --git a/cx_Oracle.c b/cx_Oracle.c new file mode 100644 index 0000000..2361198 --- /dev/null +++ b/cx_Oracle.c @@ -0,0 +1,472 @@ +//----------------------------------------------------------------------------- +// cx_Oracle.c +// Shared library for use by Python. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +// define whether or not we are building Oracle 9i +#ifdef OCI_ATTR_MODULE +#define ORACLE_10G +#endif +#if OCI_FNCODE_STMTRELEASE +#define ORACLE_9I +#endif + +// define whether or not we are building with the native datetime module +#if (PY_VERSION_HEX >= 0x02040000) +#include +#define NATIVE_DATETIME +#endif + +// PY_LONG_LONG was called LONG_LONG before Python 2.3 +#ifndef PY_LONG_LONG +#define PY_LONG_LONG LONG_LONG +#endif + +// define Py_ssize_t for versions before Python 2.5 +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + +// define macro for adding OCI constants +#define ADD_OCI_CONSTANT(x) \ + if (PyModule_AddIntConstant(module, #x, OCI_ ##x) < 0) \ + return; + +// define macro for adding type objects +#define ADD_TYPE_OBJECT(name, type) \ + Py_INCREF(type); \ + if (PyModule_AddObject(module, name, (PyObject*) type) < 0) \ + return; + +// define macro to get the build version as a string +#define xstr(s) str(s) +#define str(s) #s +#define BUILD_VERSION_STRING xstr(BUILD_VERSION) + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +#ifdef WITH_THREAD +static PyInterpreterState *g_InterpreterState; +#endif +static PyObject *g_WarningException = NULL; +static PyObject *g_ErrorException = NULL; +static PyObject *g_InterfaceErrorException = NULL; +static PyObject *g_DatabaseErrorException = NULL; +static PyObject *g_DataErrorException = NULL; +static PyObject *g_OperationalErrorException = NULL; +static PyObject *g_IntegrityErrorException = NULL; +static PyObject *g_InternalErrorException = NULL; +static PyObject *g_ProgrammingErrorException = NULL; +static PyObject *g_NotSupportedErrorException = NULL; +static PyTypeObject *g_DateTimeType = NULL; +static PyTypeObject *g_DecimalType = NULL; + + +//----------------------------------------------------------------------------- +// SetException() +// Create an exception and set it in the provided dictionary. +//----------------------------------------------------------------------------- +static int SetException( + PyObject *module, // module object + PyObject **exception, // exception to create + char *name, // name of the exception + PyObject *baseException) // exception to base exception on +{ + char buffer[100]; + + sprintf(buffer, "cx_Oracle.%s", name); + *exception = PyErr_NewException(buffer, baseException, NULL); + if (!*exception) + return -1; + return PyModule_AddObject(module, name, *exception); +} + + +//----------------------------------------------------------------------------- +// GetModuleAndName() +// Return the module and name for the type. +//----------------------------------------------------------------------------- +static int GetModuleAndName( + PyTypeObject *type, // type to get module/name for + PyObject **module, // name of module + PyObject **name) // name of type +{ + *module = PyObject_GetAttrString( (PyObject*) type, "__module__"); + if (!*module) + return -1; + *name = PyObject_GetAttrString( (PyObject*) type, "__name__"); + if (!*name) { + Py_DECREF(*module); + return -1; + } + return 0; +} + + +#include "Environment.c" +#ifdef ORACLE_9I +#include "SessionPool.c" +#else +#include "Connection.c" +#endif + + +//----------------------------------------------------------------------------- +// MakeDSN() +// Make a data source name given the host port and SID. +//----------------------------------------------------------------------------- +static PyObject* MakeDSN( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ + char *host, *sid; + int port; + + // parse arguments + if (!PyArg_ParseTuple(args, "sis", &host, &port, &sid)) + return NULL; + + // return the formatted string + return PyString_FromFormat("(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" + "(PROTOCOL=TCP)(HOST=%s)(PORT=%d)))(CONNECT_DATA=(SID=%s)))", + host, port, sid); +} + + +//----------------------------------------------------------------------------- +// Time() +// Returns a time value suitable for binding. +//----------------------------------------------------------------------------- +static PyObject* Time( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ + PyErr_SetString(g_NotSupportedErrorException, + "Oracle does not support time only variables"); + return NULL; +} + + +//----------------------------------------------------------------------------- +// TimeFromTicks() +// Returns a time value suitable for binding. +//----------------------------------------------------------------------------- +static PyObject* TimeFromTicks( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ + PyErr_SetString(g_NotSupportedErrorException, + "Oracle does not support time only variables"); + return NULL; +} + + +#ifndef NATIVE_DATETIME +//----------------------------------------------------------------------------- +// Date() +// Returns a date value suitable for binding. +//----------------------------------------------------------------------------- +static PyObject* Date( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ + return ExternalDateTimeVar_New(&g_ExternalDateTimeVarType, args, NULL); +} +#endif + + +//----------------------------------------------------------------------------- +// DateFromTicks() +// Returns a date value suitable for binding. +//----------------------------------------------------------------------------- +static PyObject* DateFromTicks( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ +#ifdef NATIVE_DATETIME + return PyDate_FromTimestamp(args); +#else + struct tm *time; + time_t ticks; + int ticksArg; + + if (!PyArg_ParseTuple(args, "i", &ticksArg)) + return NULL; + + ticks = ticksArg; + time = localtime(&ticks); + if (time == NULL) + return PyErr_SetFromErrno(g_DataErrorException); + return ExternalDateTimeVar_NewFromC(&g_ExternalDateTimeVarType, + time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, 0, 0, 0, 0); +#endif +} + + +//----------------------------------------------------------------------------- +// TimestampFromTicks() +// Returns a date value suitable for binding. +//----------------------------------------------------------------------------- +static PyObject* TimestampFromTicks( + PyObject* self, // passthrough argument + PyObject* args) // arguments to function +{ +#ifdef NATIVE_DATETIME + return PyDateTime_FromTimestamp(args); +#else + struct tm *time; + time_t ticks; + int ticksArg; + + if (!PyArg_ParseTuple(args, "i", &ticksArg)) + return NULL; + + ticks = ticksArg; + time = localtime(&ticks); + if (time == NULL) + return PyErr_SetFromErrno(g_DataErrorException); + return ExternalDateTimeVar_NewFromC(&g_ExternalDateTimeVarType, + time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec, 0); +#endif +} + + +//----------------------------------------------------------------------------- +// Declaration of methods supported by this module +//----------------------------------------------------------------------------- +static PyMethodDef g_ModuleMethods[] = { + { "makedsn", MakeDSN, METH_VARARGS }, +#ifndef NATIVE_DATETIME + { "Date", Date, METH_VARARGS }, +#endif + { "Time", Time, METH_VARARGS }, + { "DateFromTicks", DateFromTicks, METH_VARARGS }, + { "TimeFromTicks", TimeFromTicks, METH_VARARGS }, + { "TimestampFromTicks", TimestampFromTicks, METH_VARARGS }, + { NULL } +}; + +//----------------------------------------------------------------------------- +// initcx_Oracle() +// Initialization routine for the shared libary. +//----------------------------------------------------------------------------- +void initcx_Oracle(void) +{ + PyThreadState *threadState; + PyObject *module; + + // initialize the interpreter state for callbacks +#ifdef WITH_THREAD + PyEval_InitThreads(); + threadState = PyThreadState_Swap(NULL); + if (!threadState) + return; + g_InterpreterState = threadState->interp; + PyThreadState_Swap(threadState); +#endif + + // manage the use of the datetime module; in Python 2.4 and up, the native + // datetime type is used since a C API is exposed; in all other cases, the + // internal datetime type is used and if the datetime module exists, + // binding to that type is permitted +#ifdef NATIVE_DATETIME + PyDateTime_IMPORT; + if (PyErr_Occurred()) + return; +#else + module = PyImport_ImportModule("datetime"); + if (module) + g_DateTimeType = (PyTypeObject*) PyObject_GetAttrString(module, + "datetime"); + PyErr_Clear(); +#endif + + // attempt to import the decimal module + module = PyImport_ImportModule("decimal"); + if (module) + g_DecimalType = (PyTypeObject*) PyObject_GetAttrString(module, + "Decimal"); + PyErr_Clear(); + + // prepare the types for use by the module + if (PyType_Ready(&g_ConnectionType) < 0) + return; + if (PyType_Ready(&g_CursorType) < 0) + return; + if (PyType_Ready(&g_ErrorType) < 0) + return; +#ifndef NATIVE_DATETIME + if (PyType_Ready(&g_ExternalDateTimeVarType) < 0) + return; +#endif +#ifdef ORACLE_9I + if (PyType_Ready(&g_SessionPoolType) < 0) + return; + if (PyType_Ready(&g_TimestampVarType) < 0) + return; +#endif + if (PyType_Ready(&g_EnvironmentType) < 0) + return; + if (PyType_Ready(&g_ObjectTypeType) < 0) + return; + if (PyType_Ready(&g_ObjectAttributeType) < 0) + return; + if (PyType_Ready(&g_StringVarType) < 0) + return; + if (PyType_Ready(&g_FixedCharVarType) < 0) + return; + if (PyType_Ready(&g_RowidVarType) < 0) + return; + if (PyType_Ready(&g_BinaryVarType) < 0) + return; + if (PyType_Ready(&g_LongStringVarType) < 0) + return; + if (PyType_Ready(&g_LongBinaryVarType) < 0) + return; + if (PyType_Ready(&g_NumberVarType) < 0) + return; + if (PyType_Ready(&g_ExternalLobVarType) < 0) + return; + if (PyType_Ready(&g_DateTimeVarType) < 0) + return; + if (PyType_Ready(&g_CLOBVarType) < 0) + return; + if (PyType_Ready(&g_NCLOBVarType) < 0) + return; + if (PyType_Ready(&g_BLOBVarType) < 0) + return; + if (PyType_Ready(&g_BFILEVarType) < 0) + return; + if (PyType_Ready(&g_CursorVarType) < 0) + return; + if (PyType_Ready(&g_ObjectVarType) < 0) + return; + if (PyType_Ready(&g_ExternalObjectVarType) < 0) + return; + + // initialize module and retrieve the dictionary + module = Py_InitModule("cx_Oracle", g_ModuleMethods); + if (!module) + return; + + // create exception object and add it to the dictionary + if (SetException(module, &g_WarningException, + "Warning", PyExc_StandardError) < 0) + return; + if (SetException(module, &g_ErrorException, + "Error", PyExc_StandardError) < 0) + return; + if (SetException(module, &g_InterfaceErrorException, + "InterfaceError", g_ErrorException) < 0) + return; + if (SetException(module, &g_DatabaseErrorException, + "DatabaseError", g_ErrorException) < 0) + return; + if (SetException(module, &g_DataErrorException, + "DataError", g_DatabaseErrorException) < 0) + return; + if (SetException(module, &g_OperationalErrorException, + "OperationalError", g_DatabaseErrorException) < 0) + return; + if (SetException(module, &g_IntegrityErrorException, + "IntegrityError", g_DatabaseErrorException) < 0) + return; + if (SetException(module, &g_InternalErrorException, + "InternalError", g_DatabaseErrorException) < 0) + return; + if (SetException(module, &g_ProgrammingErrorException, + "ProgrammingError", g_DatabaseErrorException) < 0) + return; + if (SetException(module, &g_NotSupportedErrorException, + "NotSupportedError", g_DatabaseErrorException) < 0) + return; + + // set up the types that are available + ADD_TYPE_OBJECT("Binary", &PyBuffer_Type) + ADD_TYPE_OBJECT("Connection", &g_ConnectionType) + ADD_TYPE_OBJECT("Cursor", &g_CursorType) +#ifdef NATIVE_DATETIME + ADD_TYPE_OBJECT("Timestamp", PyDateTimeAPI->DateTimeType) + ADD_TYPE_OBJECT("Date", PyDateTimeAPI->DateType) +#else + ADD_TYPE_OBJECT("Timestamp", &g_ExternalDateTimeVarType) +#endif +#ifdef ORACLE_9I + ADD_TYPE_OBJECT("SessionPool", &g_SessionPoolType) +#endif + ADD_TYPE_OBJECT("_Error", &g_ErrorType) + + // the name "connect" is required by the DB API + ADD_TYPE_OBJECT("connect", &g_ConnectionType) + + // create the basic data types for setting input sizes + ADD_TYPE_OBJECT("BINARY", &g_BinaryVarType) + ADD_TYPE_OBJECT("BFILE", &g_BFILEVarType) + ADD_TYPE_OBJECT("BLOB", &g_BLOBVarType) + ADD_TYPE_OBJECT("CLOB", &g_CLOBVarType) + ADD_TYPE_OBJECT("CURSOR", &g_CursorVarType) + ADD_TYPE_OBJECT("OBJECT", &g_ObjectVarType) +#ifdef NATIVE_DATETIME + ADD_TYPE_OBJECT("DATETIME", PyDateTimeAPI->DateTimeType) +#else + ADD_TYPE_OBJECT("DATETIME", &g_ExternalDateTimeVarType) +#endif + ADD_TYPE_OBJECT("FIXED_CHAR", &g_FixedCharVarType) + ADD_TYPE_OBJECT("LOB", &g_ExternalLobVarType) + ADD_TYPE_OBJECT("LONG_BINARY", &g_LongBinaryVarType) + ADD_TYPE_OBJECT("LONG_STRING", &g_LongStringVarType) + ADD_TYPE_OBJECT("NCLOB", &g_NCLOBVarType) + ADD_TYPE_OBJECT("NUMBER", &g_NumberVarType) + ADD_TYPE_OBJECT("ROWID", &g_RowidVarType) + ADD_TYPE_OBJECT("STRING", &g_StringVarType) +#ifdef ORACLE_9I + ADD_TYPE_OBJECT("TIMESTAMP", &g_TimestampVarType) +#endif + + // create constants required by Python DB API 2.0 + if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0) + return; + if (PyModule_AddIntConstant(module, "threadsafety", 2) < 0) + return; + if (PyModule_AddStringConstant(module, "paramstyle", "named") < 0) + return; + + // add version and build time for easier support + if (PyModule_AddStringConstant(module, "version", + BUILD_VERSION_STRING) < 0) + return; + if (PyModule_AddStringConstant(module, "buildtime", + __DATE__ " " __TIME__) < 0) + return; + + // add constants for registering callbacks + ADD_OCI_CONSTANT(SYSDBA) + ADD_OCI_CONSTANT(SYSOPER) + ADD_OCI_CONSTANT(FNCODE_BINDBYNAME) + ADD_OCI_CONSTANT(FNCODE_BINDBYPOS) + ADD_OCI_CONSTANT(FNCODE_DEFINEBYPOS) + ADD_OCI_CONSTANT(FNCODE_STMTEXECUTE) + ADD_OCI_CONSTANT(FNCODE_STMTFETCH) + ADD_OCI_CONSTANT(FNCODE_STMTPREPARE) + ADD_OCI_CONSTANT(UCBTYPE_ENTRY) + ADD_OCI_CONSTANT(UCBTYPE_EXIT) + ADD_OCI_CONSTANT(UCBTYPE_REPLACE) +#ifdef ORACLE_9I + ADD_OCI_CONSTANT(SPOOL_ATTRVAL_WAIT) + ADD_OCI_CONSTANT(SPOOL_ATTRVAL_NOWAIT) + ADD_OCI_CONSTANT(SPOOL_ATTRVAL_FORCEGET) +#endif +} + diff --git a/html/about.html b/html/about.html new file mode 100644 index 0000000..a62a95f --- /dev/null +++ b/html/about.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + +About this document ... + + + + + +

+About this document ... +

+ cx_Oracle, +April 18, 2007, Release 4.3.1 +

This document was generated using the + LaTeX2HTML translator. +

+ +

+ LaTeX2HTML is Copyright © + 1993, 1994, 1995, 1996, 1997, Nikos + Drakos, Computer Based Learning Unit, University of + Leeds, and Copyright © 1997, 1998, Ross + Moore, Mathematics Department, Macquarie University, + Sydney. +

+ +

The application of + LaTeX2HTML to the Python + documentation has been heavily tailored by Fred L. Drake, + Jr. Original navigation icons were contributed by Christopher + Petrilli. +

+ + + + + + diff --git a/html/blank.png b/html/blank.png new file mode 100644 index 0000000000000000000000000000000000000000..2af5639b9ec6f5c74d9bc85d695eb1120a578844 GIT binary patch literal 1031 zcmW-g%}*0R5XGmILc!qC7!s2jFA7Pi-L|1OkP>KALIe|!wxvrN0~ja~uAbC*@nW(^ z6HX>_CjJ4Q_HN>lukuZyd>2@M-AUSJc4pqZ_a^;3Ix^JJ-q|i99ogZ`D}E!5PdLbT zX6efxk)Vve7#}1J1OiP>O~GKWxw$zM3bnMfw6?Z}!{N5JHmXWQix?3r;(V@@XeCC8 zRpNYEY0+AY7OTbiZe>IpF-EKr=ZBRQZN*rzR-DIDPP7x_#5!>vS_w#u(o2!75n3su zlvT?4#pGHmqm|Xlc^;ItQN}21l=HOc)>av-tX0mlWLP_8oU%?iPZ|-hh9|?r7Lz-FLhjGOxiBZ?h8&G6aTe}?@i>jO zc!?dbWEej%0L4gzG+3~PCcGfRW}v|ajA`1b^FXYwuCA@Et*@_dY;0_9ZdNLlt*x!? z?d_eNo!#Bty}iBt{r!W3gTuqaYPEWFbaZ@tT&vYiPEJlwPtVTI&d<*;E-o%FFR!ky z>h=2d_4Uoo&F$?i$OHYIe;faS{PUrVd)6k?OI;$r znkJ%wAC+Y~2FinP%40LS{2@16l)lH0o@A$Tlf{?0$x`86BoU2Adn4&|Ppr2m_B2wM zEzOk|`=%D>%B4cII2WCme?K!_juvL7zWsi<0$i7eD@{)4%JbmGa)neXR!pSQI0C0IOr??&Iv!8t68U27&i6V~cN + + + + + + + + + + + + + +2. Connection Objects + + + + + +

+
+2. Connection Objects +

+ +

+NOTE: Any outstanding changes will be rolled back when the connection +object is destroyed or closed. + +

+

action
+
+ This write-only attribute sets the action column in the v$session table and + is only available in Oracle 10g. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

+ +
begin()
+
+
+ +
begin([formatId, transactionId, + branchId])
+
Explicitly begin a new transaction. Without parameters, this explicitly + begins a local transaction; otherwise, this explicitly begins a distributed + (global) transaction with the given parameters. See the Oracle documentation + for more details. + +

+Note that in order to make use of global (distributed) transactions, the + twophase argument to the Connection constructor must be a true value. See the + comments on the Connection constructor for more information (1). + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

+ +
cancel()
+
+ Cancel a long-running transaction. This is only effective on non-Windows + platforms. +
+ +

+

clientinfo
+
+ This write-only attribute sets the client_info column in the v$session table + and is only available in Oracle 10g. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

+ +
close()
+
+ Close the connection now, rather than whenever __del__ is called. The + connection will be unusable from this point forward; an Error exception will + be raised if any operation is attempted with the connection. The same applies + to any cursor objects trying to use the connection. +
+ +

+

+ +
commit()
+
+ Commit any pending transactions to the database. +
+ +

+

+ +
cursor()
+
+ Return a new Cursor object (3) using the connection. +
+ +

+

dsn
+
+ This read-only attribute returns the TNS entry of the database to which a + connection has been established. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

encoding
+
+ This read-only attribute returns the IANA character set name of the character + set in use by the Oracle client. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

maxBytesPerCharacter
+
+ This read-only attribute returns the maximum number of bytes each character + can use for the client character set. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

module
+
+ This write-only attribute sets the module column in the v$session table and + is only available in Oracle 10g. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

nencoding
+
+ This read-only attribute returns the IANA character set name of the national + character set in use by the Oracle client. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

password
+
+ This read-only attribute returns the password of the user which established + the connection to the database. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

+ +
prepare()
+
+ Prepare the distributed (global) transaction for commit. + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

+ +
register(code, when, function)
+
+ Register the function as an OCI callback. The code is one of the function + codes defined in the Oracle documentation of which the most common ones are + defined as constants in this module. The when parameter is one of + UCBTYPE_ENTRY, UCBTYPE_EXIT or UCBTYPE_REPLACE. The function is a Python + function which will accept the parameters that the OCI function accepts, + modified as needed to return Python objects that are of some use. Note that + this is a highly experimental method and can cause cx_Oracle to crash if not + used properly. In particular, the OCI does not provide sizing information to + the callback so attempts to access a variable beyond the allocated size will + crash cx_Oracle. Use with caution. + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

+ +
rollback()
+
+ Rollback any pending transactions. +
+ +

+

tnsentry
+
+ This read-only attribute returns the TNS entry of the database to which a + connection has been established. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

+ +
unregister(code, when)
+
+ Unregister the function as an OCI callback. The code is one of the function + codes defined in the Oracle documentation of which the most common ones are + defined as constants in this module. The when parameter is one of + UCBTYPE_ENTRY, UCBTYPE_EXIT or UCBTYPE_REPLACE. + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

username
+
+ This read-only attribute returns the name of the user which established the + connection to the database. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

version
+
+ This read-only attribute returns the version of the database to which a + connection has been established. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+ +

+ + + + diff --git a/html/contents.html b/html/contents.html new file mode 100644 index 0000000..a235c44 --- /dev/null +++ b/html/contents.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + +Contents + + + + +

+Contents +

+ + + + +

+ +

+ + + + diff --git a/html/contents.png b/html/contents.png new file mode 100644 index 0000000000000000000000000000000000000000..3429be0c1d45d9b1f0816da78621c029f512de1e GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUu&Ir;B4q#jUfG?oK+az|q>>#C1&m-v3l_L(kpjZ++9#jb~Q4+3_XR z9B6<3v7g}&LrNuo%Gvh}Ijg4ntJ`I`J0-45WM0x7CTzi@Ju7TZTjSpMLMyILf7xB8 zzpLlj9fsp zeuu2_;)6`NKZ_-)w2oSBzJ<5*SaeU1+jXReym>iyYq)M&223n3t=3{GA}JF@a0-?ty6= z4|59K_IAJPaC-WhpW+ApmRY3ca0~EUaO{{dzu`0k-;rwzOJ=t`j&$K^?=t zlYFakZp7OHL&~QrB%&lZKdq!Zu_%>+q2gBYg@9{c&iMNHfA#e_?{hB9;G*#*uns|DeZt?IkVPWWX<(OZvdC7C2 sc5#sQfW&mLlZ?$)8JJx%yI{t^z%`5Ur+fKfQJ@kAPgg&ebxsLQ0It{kg8%>k literal 0 HcmV?d00001 diff --git a/html/cursorobj.html b/html/cursorobj.html new file mode 100644 index 0000000..9752678 --- /dev/null +++ b/html/cursorobj.html @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + +3. Cursor Objects + + + + + +

+
+3. Cursor Objects +

+ +

+

arraysize
+
+ This read-write attribute specifies the number of rows to fetch at a time + internally and is the default number of rows to fetch with the fetchmany() + call. It defaults to 1 meaning to fetch a single row at a time. Note that + this attribute can drastically affect the performance of a query since it + directly affects the number of network round trips that need to be performed. +
+ +

+

bindarraysize
+
+ This read-write attribute specifies the number of rows to bind at a time and + is used when creating variables via setinputsizes() or var(). It defaults to + 1 meaning to bind a single row at a time. + +

+NOTE: The DB API definition does not define this attribute. +

+ +

+

+ +
arrayvar(dataType, value, [size])
+
+ Create an array variable associated with the cursor of the given type and + size and return a variable object (4). The value is either an + integer specifying the number of elements to allocate or it is a list and + the number of elements allocated is drawn from the size of the list. If the + value is a list, the variable is also set with the contents of the list. If + the size is not specified and the type is a string or binary, 4000 bytes + (maximum allowable by Oracle) is allocated. This is needed for passing arrays + to PL/SQL (in cases where the list might be empty and the type cannot be + determined automatically) or returning arrays from PL/SQL. + +

+NOTE: The DB API definition does not define this method. +

+ +

+

+ +
bindnames()
+
+ Return the list of bind variable names bound to the statement. Note that the + statement must have been prepared first. + +

+NOTE: The DB API definition does not define this method. +

+ +

+

+ +
callfunc(name, returnType, + [parameters=[]])
+
+ Call a function with the given name. The return type is specified in the + same notation as is required by setinputsizes(). The sequence of parameters + must contain one entry for each argument that the function expects. + The result of the call is the return value of the function. +
+ +

+

+ +
callproc(name, [parameters=[]])
+
+ Call a procedure with the given name. The sequence of parameters must contain + one entry for each argument that the procedure expects. The result of the + call is a modified copy of the input sequence. Input parameters are left + untouched; output and input/output parameters are replaced with possibly new + values. +
+ +

+

+ +
close()
+
+ Close the cursor now, rather than whenever __del__ is called. The cursor will + be unusable from this point forward; an Error exception will be raised if any + operation is attempted with the cursor. +
+ +

+

connection
+
+ This read-only attribute returns a reference to the connection object on + which the cursor was created. + +

+NOTE: This attribute is an extension to the DB API definition but it + is mentioned in PEP 249 as an optional extension. +

+ +

+

description
+
+ This read-only attribute is a sequence of 7-item sequences. Each of these + sequences contains information describing one result column: (name, type, + display_size, internal_size, precision, scale, null_ok). This attribute will + be None for operations that do not return rows or if the cursor has not had + an operation invoked via the execute() method yet. + +

+The type will be one of the type objects defined at the module level. +

+ +

+

+ +
execute(statement, [parameters], + **keywordParameters)
+
+ Execute a statement against the database. Parameters may be passed as a + dictionary or sequence or as keyword arguments. If the arguments are a + dictionary, the values will be bound by name and if the arguments are a + sequence the values will be bound by position. + +

+A reference to the statement will be retained by the cursor. If None or the + same string object is passed in again, the cursor will execute that + statement again without performing a prepare or rebinding and redefining. + This is most effective for algorithms where the same statement is used, but + different parameters are bound to it (many times). + +

+For maximum efficiency when reusing an statement, it is best to use the + setinputsizes() method to specify the parameter types and sizes ahead of + time; in particular, None is assumed to be a string of length 1 so any + values that are later bound as numbers or dates will raise a TypeError + exception. + +

+If the statement is a query, a list of variable objects (4) will + be returned corresponding to the list of variables into which data will be + fetched with the fetchone(), fetchmany() and fetchall() methods; otherwise, + None will be returned. + +

+NOTE: The DB API definition does not define the return value of this + method. +

+ +

+

+ +
executemany(statement, parameters)
+
+ Prepare a statement for execution against a database and then execute it + against all parameter mappings or sequences found in the sequence parameters. + The statement is managed in the same way as the execute() method manages it. +
+ +

+

+ +
executemanyprepared(numIters)
+
+ Execute the previously prepared and bound statement the given number of + times. The variables that are bound must have already been set to their + desired value before this call is made. This method was designed for the + case where optimal performance is required as it comes at the expense of + compatibility with the DB API. + +

+NOTE: The DB API definition does not define this method. +

+ +

+

+ +
fetchall()
+
+ Fetch all (remaining) rows of a query result, returning them as a list of + tuples. An empty list is returned if no more rows are available. Note that + the cursor's arraysize attribute can affect the performance of this + operation, as internally reads from the database are done in batches + corresponding to the arraysize. + +

+An exception is raised if the previous call to execute() did not produce any + result set or no call was issued yet. +

+ +

+

+ +
fetchmany([numRows=cursor.arraysize])
+
+ Fetch the next set of rows of a query result, returning a list of tuples. An + empty list is returned if no more rows are available. Note that the cursor's + arraysize attribute can affect the performance of this operation. + +

+The number of rows to fetch is specified by the parameter. If it is not + given, the cursor's arrysize attribute determines the number of rows to be + fetched. If the number of rows available to be fetched is fewer than the + amount requested, fewer rows will be returned. + +

+An exception is raised if the previous call to execute() did not produce any + result set or no call was issued yet. +

+ +

+

+ +
fetchone()
+
+ Fetch the next row of a query result set, returning a single tuple or None + when no more data is available. + +

+An exception is raised if the previous call to execute() did not produce any + result set or no call was issued yet. +

+ +

+

+ +
fetchraw([numRows=cursor.arraysize])
+
+ Fetch the next set of rows of a query result into the internal buffers of the + defined variables for the cursor. The number of rows actually fetched is + returned. This method was designed for the case where optimal performance is + required as it comes at the expense of compatibility with the DB API. + +

+An exception is raised if the previous call to execute() did not produce any + result set or no call was issued yet. + +

+NOTE: The DB API definition does not define this method. +

+ +

+

+ +
__iter__()
+
+ Returns the cursor itself to be used as an iterator. + +

+NOTE: This method is an extension to the DB API definition but it + is mentioned in PEP 249 as an optional extension. +

+ +

+

+ +
next()
+
+ Fetch the next row of a query result set, using the same semantics as + the method fetchone(). + +

+NOTE: This method is an extension to the DB API definition but it + is mentioned in PEP 249 as an optional extension. +

+ +

+

numbersAsStrings
+
+ This integer attribute defines whether or not numbers should be returned as + strings rather than integers or floating point numbers. This is useful to get + around the fact that Oracle floating point numbers have considerably greater + precision than C floating point numbers and not require a change to the SQL + being executed. + +

+NOTE: The DB API definition does not define this attribute. +

+ +

+

+ +
parse(statement)
+
+ This can be used to parse a statement without actually executing it (this + step is done automatically by Oracle when a statement is executed). + +

+NOTE: The DB API definition does not define this method. +

+ +

+

+ +
prepare(statement)
+
+ This can be used before a call to execute() to define the statement that will + be executed. When this is done, the prepare phase will not be performed when + the call to execute() is made with None or the same string object as the + statement. + +

+NOTE: The DB API definition does not define this method. +

+ +

+

rowcount
+
+ This read-only attribute specifies the number of rows that have currently + been fetched from the cursor (for select statements) or that have been + affected by the operation (for insert, update and delete statements). +
+ +

+

+ +
setinputsizes(*args, **keywordArgs)
+
+ This can be used before a call to execute() to predefine memory areas for the + operation's parameters. Each parameter should be a type object corresponding + to the input that will be used or it should be an integer specifying the + maximum length of a string parameter. Use keyword arguments when binding by + name and positional arguments when binding by position. The singleton None + can be used as a parameter when using positional arguments to indicate that + no space should be reserved for that position. +
+ +

+

+ +
setoutputsize(size, [column])
+
+ This can be used before a call to execute() to predefine memory areas for the + long columns that will be fetched. The column is specified as an index into + the result sequence. Not specifying the column will set the default size for + all large columns in the cursor. +
+ +

+

statement
+
+ This read-only attribute provides the string object that was previously + prepared with prepare() or executed with execute(). + +

+NOTE: The DB API definition does not define this attribute. +

+ +

+

+ +
var(dataType, [size])
+
+ Create a variable associated with the cursor of the given type and size and + return a variable object (4). If the size is not specified and the + type is a string or binary, 4000 bytes (maximum allowable by Oracle) is + allocated; if the size is not specified and the type is a long string or long + binary, 128KB is allocated. This method was designed for use with PL/SQL + in/out variables where the length or type cannot be determined automatically + from the Python object passed in. + +

+NOTE: The DB API definition does not define this method. +

+ +

+ +

+ + + + diff --git a/html/cx_Oracle.css b/html/cx_Oracle.css new file mode 100644 index 0000000..06a613c --- /dev/null +++ b/html/cx_Oracle.css @@ -0,0 +1,243 @@ +/* + * The first part of this is the standard CSS generated by LaTeX2HTML, + * with the "empty" declarations removed. + */ + +/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ +.math { font-family: "Century Schoolbook", serif; } +.math i { font-family: "Century Schoolbook", serif; + font-weight: bold } +.boldmath { font-family: "Century Schoolbook", serif; + font-weight: bold } + +/* + * Implement both fixed-size and relative sizes. + * + * I think these can be safely removed, as it doesn't appear that + * LaTeX2HTML ever generates these, even though these are carried + * over from the LaTeX2HTML stylesheet. + */ +small.xtiny { font-size : xx-small; } +small.tiny { font-size : x-small; } +small.scriptsize { font-size : smaller; } +small.footnotesize { font-size : small; } +big.xlarge { font-size : large; } +big.xxlarge { font-size : x-large; } +big.huge { font-size : larger; } +big.xhuge { font-size : xx-large; } + +/* + * Document-specific styles come next; + * these are added for the Python documentation. + * + * Note that the size specifications for the H* elements are because + * Netscape on Solaris otherwise doesn't get it right; they all end up + * the normal text size. + */ + +body { color: #000000; + background-color: #ffffff; } + +a:link:active { color: #ff0000; } +a:link:hover { background-color: #bbeeff; } +a:visited:hover { background-color: #bbeeff; } +a:visited { color: #551a8b; } +a:link { color: #0000bb; } + +h1, h2, h3, h4, h5, h6 { font-family: avantgarde, sans-serif; + font-weight: bold; } +h1 { font-size: 180%; } +h2 { font-size: 150%; } +h3, h4 { font-size: 120%; } + +/* These are section titles used in navigation links, so make sure we + * match the section header font here, even it not the weight. + */ +.sectref { font-family: avantgarde, sans-serif; } +/* And the label before the titles in navigation: */ +.navlabel { font-size: 85%; } + + +/* LaTeX2HTML insists on inserting
elements into headers which + * are marked with \label. This little bit of CSS magic ensures that + * these elements don't cause spurious whitespace to be added. + */ +h1>br, h2>br, h3>br, +h4>br, h5>br, h6>br { display: none; } + +code, tt { font-family: "lucida typewriter", lucidatypewriter, + monospace; } +var { font-family: times, serif; + font-style: italic; + font-weight: normal; } + +.Unix { font-variant: small-caps; } + +.typelabel { font-family: lucida, sans-serif; } + +.navigation td { background-color: #99ccff; + font-weight: bold; + font-family: avantgarde, sans-serif; + font-size: 110%; } + +div.warning { background-color: #fffaf0; + border: thin solid black; + padding: 1em; + margin-left: 2em; + margin-right: 2em; } + +div.warning .label { font-family: sans-serif; + font-size: 110%; + margin-right: 0.5em; } + +div.note { background-color: #fffaf0; + border: thin solid black; + padding: 1em; + margin-left: 2em; + margin-right: 2em; } + +div.note .label { margin-right: 0.5em; + font-family: sans-serif; } + +address { font-size: 80%; } +.release-info { font-style: italic; + font-size: 80%; } + +.titlegraphic { vertical-align: top; } + +.verbatim pre { color: #00008b; + font-family: "lucida typewriter", lucidatypewriter, + monospace; + font-size: 90%; } +.verbatim { margin-left: 2em; } +.verbatim .footer { padding: 0.05in; + font-size: 85%; + background-color: #99ccff; + margin-right: 0.5in; } + +.grammar { background-color: #99ccff; + margin-right: 0.5in; + padding: 0.05in; } +.grammar-footer { padding: 0.05in; + font-size: 85%; } +.grammartoken { font-family: "lucida typewriter", lucidatypewriter, + monospace; } + +.productions { background-color: #bbeeff; } +.productions a:active { color: #ff0000; } +.productions a:link:hover { background-color: #99ccff; } +.productions a:visited:hover { background-color: #99ccff; } +.productions a:visited { color: #551a8b; } +.productions a:link { color: #0000bb; } +.productions table { vertical-align: baseline; + empty-cells: show; } +.productions > table td, +.productions > table th { padding: 2px; } +.productions > table td:first-child, +.productions > table td:last-child { + font-family: "lucida typewriter", + lucidatypewriter, + monospace; + } +/* same as the second selector above, but expressed differently for Opera */ +.productions > table td:first-child + td + td { + font-family: "lucida typewriter", + lucidatypewriter, + monospace; + vertical-align: baseline; + } +.productions > table td:first-child + td { + padding-left: 1em; + padding-right: 1em; + } +.productions > table tr { vertical-align: baseline; } + +.email { font-family: avantgarde, sans-serif; } +.mailheader { font-family: avantgarde, sans-serif; } +.mimetype { font-family: avantgarde, sans-serif; } +.newsgroup { font-family: avantgarde, sans-serif; } +.url { font-family: avantgarde, sans-serif; } +.file { font-family: avantgarde, sans-serif; } +.guilabel { font-family: avantgarde, sans-serif; } + +.realtable { border-collapse: collapse; + border-color: black; + border-style: solid; + border-width: 0px 0px 2px 0px; + empty-cells: show; + margin-left: auto; + margin-right: auto; + padding-left: 0.4em; + padding-right: 0.4em; + } +.realtable tbody { vertical-align: baseline; } +.realtable tfoot { display: table-footer-group; } +.realtable thead { background-color: #99ccff; + border-width: 0px 0px 2px 1px; + display: table-header-group; + font-family: avantgarde, sans-serif; + font-weight: bold; + vertical-align: baseline; + } +.realtable thead :first-child { + border-width: 0px 0px 2px 0px; + } +.realtable thead th { border-width: 0px 0px 2px 1px } +.realtable td, +.realtable th { border-color: black; + border-style: solid; + border-width: 0px 0px 1px 1px; + padding-left: 0.4em; + padding-right: 0.4em; + } +.realtable td:first-child, +.realtable th:first-child { + border-left-width: 0px; + vertical-align: baseline; + } +.center { text-align: center; } +.left { text-align: left; } +.right { text-align: right; } + +.refcount-info { font-style: italic; } +.refcount-info .value { font-weight: bold; + color: #006600; } + +/* + * Some decoration for the "See also:" blocks, in part inspired by some of + * the styling on Lars Marius Garshol's XSA pages. + * (The blue in the navigation bars is #99CCFF.) + */ +.seealso { background-color: #fffaf0; + border: thin solid black; + padding: 0pt 1em 4pt 1em; } + +.seealso > .heading { font-size: 110%; + font-weight: bold; } + +/* + * Class 'availability' is used for module availability statements at + * the top of modules. + */ +.availability .platform { font-weight: bold; } + + +/* + * Additional styles for the distutils package. + */ +.du-command { font-family: monospace; } +.du-option { font-family: avantgarde, sans-serif; } +.du-filevar { font-family: avantgarde, sans-serif; + font-style: italic; } +.du-xxx:before { content: "** "; + font-weight: bold; } +.du-xxx:after { content: " **"; + font-weight: bold; } + + +/* + * Some specialization for printed output. + */ +@media print { + .online-navigation { display: none; } + } diff --git a/html/cx_Oracle.html b/html/cx_Oracle.html new file mode 100644 index 0000000..b518515 --- /dev/null +++ b/html/cx_Oracle.html @@ -0,0 +1,119 @@ + + + + + + + + + + + +cx_Oracle + + + + + +

+ +

+
+

cx_Oracle

+

Anthony Tuininga

+

+ Computronix +
+Email: +

+

Release 4.3.1
+April 18, 2007

+

+
+
+ +

+ +



+ + + + + + + diff --git a/html/dateobj.html b/html/dateobj.html new file mode 100644 index 0000000..aca9888 --- /dev/null +++ b/html/dateobj.html @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + +7. Date Objects + + + + + +

+
+7. Date Objects +

+ +

+NOTE: This object is an extension the DB API. It is returned whenever +Oracle date and timestamp (in Oracle 9i) columns are fetched and whenever the +constructor methods (Date(), Time(), Timestamp()) are called. + +

+NOTE: As of Python 2.4 cx_Oracle returns the datetime objects from the +standard library datetime module instead of these objects. + +

+

year
+
+ This read-only attribute returns the year. +
+ +

+

month
+
+ This read-only attribute returns the month. +
+ +

+

day
+
+ This read-only attribute returns the day. +
+ +

+

hour
+
+ This read-only attribute returns the hour. +
+ +

+

minute
+
+ This read-only attribute returns the minute. +
+ +

+

second
+
+ This read-only attribute returns the second. +
+ +

+

fsecond
+
+ This read-only attribute returns the fractional second. +
+ +

+ +

+ + + + diff --git a/html/front.html b/html/front.html new file mode 100644 index 0000000..369df8b --- /dev/null +++ b/html/front.html @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + +Front Matter + + + + + +

+
+Front Matter +

+ +

+Copyright © 2001-2007 Computronix. +All rights reserved. + +

+See the end of this document for complete license and permissions +information. + +

+ +

Abstract:

+
+ +

+cx_Oracle is a Python extension module that allows access to Oracle and +conforms to the Python database API 2.0 specifications with a few exceptions. +See http://www.python.org/topics/database/DatabaseAPI-2.0.html for more +information on the Python database API specification. + +

+

+

+ +

+ +

+ + + + diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..b518515 --- /dev/null +++ b/html/index.html @@ -0,0 +1,119 @@ + + + + + + + + + + + +cx_Oracle + + + + + +

+ +

+
+

cx_Oracle

+

Anthony Tuininga

+

+ Computronix +
+Email: +

+

Release 4.3.1
+April 18, 2007

+

+
+
+ +

+ +



+ + + + + + + diff --git a/html/index.png b/html/index.png new file mode 100644 index 0000000000000000000000000000000000000000..cd918afe76ca2913a4de7ac7663211878b0c263f GIT binary patch literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsFIJzX3_DsFAPbdm3{0tf4bMXs#u)BgXzdFjnr%UL?B*Ux+KwT(CL z+_(CGTf7E`B|d1|N*Rg2lJF1kHncD~*Ko{GLECVG&NY_%=9 z{C8Qq4_rQDCxf4A&_I-XbVGq#3K2;$RCBgY=CFO}l zsSFGiw~8+WT>Emy*T?^>ug`g(b72M-jV~DoZP;?f+~BIYL5PRmDIL9)B`4PE=$-Vr z=B<0pTT}PMdF_kd=gzY-eDM})_;UA19MD{2u(_G(d5I;Z5QB|P{fzyM{7ijK%?(U_ zOiT@p{I+=*d;1vs7`C(C@5(ZCKKbLh*2~7a755D*S literal 0 HcmV?d00001 diff --git a/html/lobobj.html b/html/lobobj.html new file mode 100644 index 0000000..006368e --- /dev/null +++ b/html/lobobj.html @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + +6. LOB Objects + + + + + +

+
+6. LOB Objects +

+ +

+NOTE: This object is an extension the DB API. It is returned whenever +Oracle CLOB, BLOB and BFILE columns are fetched. + +

+NOTE: Internally, Oracle uses LOB locators which are allocated based +on the cursor array size. Thus, it is important that the data in the LOB object +be manipulated before another internal fetch takes place. The safest way to do +this is to use the cursor as an iterator. In particular, do not use the +fetchall() method. The exception "LOB variable no longer valid after +subsequent fetch" will be raised if an attempt to access a LOB variable after +a subsequent fetch is detected. + +

+

+ +
fileexists()
+
+ Return a boolean indicating if the file referenced by the BFILE type LOB + exists. +
+ +

+

+ +
getfilename()
+
+ Return a two-tuple consisting of the directory alias and file name for a + BFILE type LOB. +
+ +

+

+ +
read([offset = 1, [amount]])
+
+ Return a portion (or all) of the data in the LOB object. +
+ +

+

+ +
setfilename(dirAlias, name)
+
+ Set the directory alias and name of the BFILE type LOB. +
+ +

+

+ +
size()
+
+ Returns the size of the data in the LOB object. +
+ +

+

+ +
trim([newSize = 0])
+
+ Trim the LOB to the new size. +
+ +

+

+ +
write(data, [offset = 1])
+
+ Write the data to the LOB object at the given offset. Note that if you want + to make the LOB value smaller, you must use the trim() function. +
+ +

+ +

+ + + + diff --git a/html/module.html b/html/module.html new file mode 100644 index 0000000..11531a8 --- /dev/null +++ b/html/module.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + +1. Module Interface + + + + + +

+
+1. Module Interface +

+ +

+

+ +
Binary(string)
+
+ Construct an object holding a binary (long) string value. +
+ +

+

+ +
Connection([user, password, + dsn, mode, handle, pool, threaded, + twophase])
+
+
+ +
connect([user, password, + dsn, mode, handle, pool, threaded, + twophase])
+
Constructor for creating a connection to the database. Return a Connection + object (2). All arguments are optional and can be specified as + keyword parameters. The dsn (data source name) is the TNS entry (from the + Oracle names server or tnsnames.ora file) or is a string like the one + returned from makedsn(). If only one parameter is passed, a connect string is + assumed which is to be of the format "user/password@dsn", the same format + accepted by Oracle applications such as SQL*Plus. If the mode is specified, + it must be one of SYSDBA or SYSOPER which are defined at the module level; + otherwise it defaults to the normal mode of connecting. If the handle is + specified, it must be of type OCISvcCtx* and is only of use when embedding + Python in an application (like PowerBuilder) which has already made the + connection. The pool is only valid in Oracle 9i and is a session pool object + (5) which is the equivalent of calling pool.acquire(). The + threaded attribute is expected to be a boolean expression which indicates + whether or not Oracle should use the mode OCI_THREADED to wrap accesses to + connections with a mutex. Doing so in single threaded applications imposes + a performance penalty of about 10-15% which is why the default is False. + The twophase attribute is expected to be a boolean expression which indicates + whether or not the attributes should be set on the connection object to allow + for two phase commit. The default for this value is also False because of + bugs in Oracle prior to Oracle 10g. +
+ +

+

+ +
Cursor(connection)
+
+ Constructor for creating a cursor. Return a new Cursor object + (3) using the connection. + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

+ +
Date(year, month, day)
+
+ Construct an object holding a date value. +
+ +

+

+ +
DateFromTicks(ticks)
+
+ Construct an object holding a date value from the given ticks value (number + of seconds since the epoch; see the documentation of the standard Python + time module for details). +
+ +

+

+ +
makedsn(host, port, sid)
+
+ Return a string suitable for use as the dsn for the connect() method. This + string is identical to the strings that are defined by the Oracle names + server or defined in the tnsnames.ora file. + +

+NOTE: This method is an extension to the DB API definition. +

+ +

+

+ +
SessionPool(user, password, database, + min, max, increment, [connectiontype, + threaded, getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT])
+
+ Create a session pool (see Oracle 9i documentation for more information) + and return a session pool object (5). This allows for very + fast connections to the database and is of primary use in a server where + the same connection is being made multiple times in rapid succession (a + web server, for example). If the connection type is specified, all calls to + acquire() will create connection objects of that type, rather than the base + type defined at the module level. The threaded attribute is expected to be a + boolean expression which indicates whether or not Oracle should use the mode + OCI_THREADED to wrap accesses to connections with a mutex. Doing so in single + threaded applications imposes a performance penalty of about 10-15% which is + why the default is False. + +

+NOTE: This method is an extension to the DB API definition and is + only available in Oracle 9i. +

+ +

+

+ +
Time(hour, minute, second)
+
+ Construct an object holding a time value. +
+ +

+

+ +
TimeFromTicks(ticks)
+
+ Construct an object holding a time value from the given ticks value (number + of seconds since the epoch; see the documentation of the standard Python + time module for details). +
+ +

+

+ +
Timestamp(year, month, day, + hour, minute, second)
+
+ Construct an object holding a time stamp value. +
+ +

+

+ +
TimestampFromTicks(ticks)
+
+ Construct an object holding a time stamp value from the given ticks value + (number of seconds since the epoch; see the documentation of the standard + Python time module for details). +
+ +

+ +



+ + + + + + + diff --git a/html/modules.png b/html/modules.png new file mode 100644 index 0000000000000000000000000000000000000000..8fa8b75524ad4072442f8f04745af27973cbc740 GIT binary patch literal 598 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsGXJY5_^DsH{Kbg<}%0tedzR@WmO6TjErOqz6e*=(Qco5{sL_|KUi z`tyvjzNWwa1E0qd*Ke$h5l^47ePi1*Lp&q*YYFS4R?A6IY+q+fpXp4$#$@n@>w@pL zH5)yBb~ilI&fDp~yXn~?`hzbNux0)FaWKvLDLKtM7)-DE-S)yE@JP zqv`*DVqYF!Kf^RB@g?Uhu?KT3kMCP`rk360(t@aBc2{2~6_L5_!foskS63vV}1V#`3H2Y)7~&VTJeCz+m2D>7h8tgf@=yhm)_9gPYwR#5E##Q zl4sT2ou>~2eeF{f5>XPIpH@ zSIiBrnj3_8=$+EhTUl~qt&ZMFpKIQ_*Ss}#Pn_4j=zZ=yE5jFWp@uJakHi7ZH3plT znVy$eQVKEHdy|2wx3RaOk%@<)k(rsdmyfZpsfm%nHWSY+2IgB07#O-=aOh>eQs4n< s7YAt%NK6Mi$=Ga_f!QUq3uX)qT(cN|x|bgo1u9|iboFyt=akR{0EAiRtN;K2 literal 0 HcmV?d00001 diff --git a/html/next.png b/html/next.png new file mode 100644 index 0000000000000000000000000000000000000000..cfe5e51ca619b357fc22d618fa2cb132036da31f GIT binary patch literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W-i+T2IDlKR^mbIJX?@mj7i?^ zE`pD?+U^2!CVRR#hE&`-JNcl{Ap;JVWQUcVoIn4spW0L=b9UR~wbf63)8v=FESgZ_ z<}j=MBX{YVz$S-xeqVU3Cs=(vpn9`0a6>DLV#H>agBAR_!G;P^{>fTwJ##~tEBGa@ ze_3bKe}lW+CX7wYQpJkVmuvmBgNq{KnG&uFeP*qF_hOBCi9zXVd)9q@PuUkd`W|8T z?D?OO|E6o##^(5>XPIpH@SIiBrnj3_8=$+EhTUl~qt&ZMFpKIQ_*Ss}#Pn_4j=zZ=yE5jFWp@uJa zkHi7ZH3plTnVy$eQVKEH*vQy$^A;Zyb7ON8Z$m>9BQrlAW5ewRranf71|B}fEDSYy z>=#b%J8=Z4T^yu6ATb^6BxAEx240W+V6sUy3)78&qol`;+0AXLa A&;S4c literal 0 HcmV?d00001 diff --git a/html/node12.html b/html/node12.html new file mode 100644 index 0000000..d5fe273 --- /dev/null +++ b/html/node12.html @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + +8. License + + + + + +

+8. License +

+ +

+

+LICENSE AGREEMENT FOR CX_ORACLE 4.3.1
+ +

+Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. +All rights reserved. + +

+Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +

+ +

    +
  1. Redistributions of source code must retain the above copyright notice, + this list of conditions, and the disclaimer that follows. + +

    +

  2. +
  3. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +

    +

  4. +
  5. Neither the name of Computronix nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. +
  6. +
+ +

+DISCLAIMER: +THIS SOFTWARE IS PROVIDED BY COMPUTRONIX AND CONTRIBUTORS *AS IS* +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COMPUTRONIX +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +

+Computronix (R) is a registered trademark of Computronix (Canada) Ltd. + +

+ +

+ + + + diff --git a/html/node4.html b/html/node4.html new file mode 100644 index 0000000..b732de7 --- /dev/null +++ b/html/node4.html @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + +1.1 Constants + + + + + +

+1.1 Constants +

+ +

+

apilevel
+
+ String constant stating the supported DB API level. Currently '2.0'. +
+ +

+

buildtime
+
+ String constant stating the time when the binary was built. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

BINARY
+
+ This type object is used to describe columns in a database that are binary + (in Oracle this is RAW columns). +
+ +

+

BFILE
+
+ This type object is used to describe columns in a database that are BFILEs. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

BLOB
+
+ This type object is used to describe columns in a database that are BLOBs. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

CLOB
+
+ This type object is used to describe columns in a database that are CLOBs. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

CURSOR
+
+ This type object is used to describe columns in a database that are cursors + (in PL/SQL these are known as ref cursors). + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

DATETIME
+
+ This type object is used to describe columns in a database that are dates. +
+ +

+

FIXED_CHAR
+
+ This type object is used to describe columns in a database that are fixed + length strings (in Oracle this is CHAR columns); these behave differently + in Oracle than varchar2 so they are differentiated here even though the DB + API does not differentiate them. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_BINDBYNAME
+
+ This constant is used to register callbacks on the OCIBindByName() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_BINDBYPOS
+
+ This constant is used to register callbacks on the OCIBindByPos() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_DEFINEBYPOS
+
+ This constant is used to register callbacks on the OCIDefineByPos() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_STMTEXECUTE
+
+ This constant is used to register callbacks on the OCIStmtExecute() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_STMTFETCH
+
+ This constant is used to register callbacks on the OCIStmtFetch() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

FNCODE_STMTPREPARE
+
+ This constant is used to register callbacks on the OCIStmtPrepare() function + of the OCI. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

LOB
+
+ This type object is the Python type of BLOB and CLOB data that is returned + from cursors. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

LONG_BINARY
+
+ This type object is used to describe columns in a database that are long + binary (in Oracle these are LONG RAW columns). + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

LONG_STRING
+
+ This type object is used to describe columns in a database that are long + strings (in Oracle these are LONG columns). + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

NCLOB
+
+ This type object is used to describe columns in a database that are NCLOBs. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

NUMBER
+
+ This type object is used to describe columns in a database that are numbers. +
+ +

+

OBJECT
+
+ This type object is used to describe columns in a database that are objects. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

paramstyle
+
+ String constant stating the type of parameter marker formatting expected by + the interface. Currently 'named' as in 'where name = :name'. +
+ +

+

ROWID
+
+ This type object is used to describe the pseudo column "rowid". +
+ +

+

SPOOL_ATTRVAL_FORCEGET
+
+ This constant is used to define the "get" mode on session pools and indicates + that a new connection will be returned if there are no free sessions + available in the pool. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

SPOOL_ATTRVAL_NOWAIT
+
+ This constant is used to define the "get" mode on session pools and indicates + that an exception is raised if there are no free sessions available in the + pool. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

SPOOL_ATTRVAL_WAIT
+
+ This constant is used to define the "get" mode on session pools and indicates + that the acquisition of a connection waits until a session is freed if there + are no free sessions available in the pool. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

STRING
+
+ This type object is used to describe columns in a database that are strings + (in Oracle this is VARCHAR2 columns). +
+ +

+

SYSDBA
+
+ Value to be passed to the connect() method which indicates that SYSDBA + access is to be acquired. See the Oracle documentation for more details. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

SYSOPER
+
+ Value to be passed to the connect() method which indicates that SYSOPER + access is to be acquired. See the Oracle documentation for more details. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

threadsafety
+
+ Integer constant stating the level of thread safety that the interface + supports. Currently 2, which means that threads may share the module and + connections, but not cursors. Sharing means that a thread may use a resource + without wrapping it using a mutex semaphore to implement resource locking. + +

+Note that in order to make use of multiple threads in a program which intends + to connect and disconnect in different threads, the threaded argument to the + Connection constructor must be a true value. See the comments on the + Connection constructor for more information (1). +

+ +

+

TIMESTAMP
+
+ This type object is used to describe columns in a database that are + timestamps. + +

+NOTE: This attribute is an extension to the DB API definition and + is only available in Oracle 9i. +

+ +

+

UCBTYPE_ENTRY
+
+ This constant is used to register callbacks on entry to the function + of the OCI. In other words, the callback will be called prior to the + execution of the OCI function. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

UCBTYPE_EXIT
+
+ This constant is used to register callbacks on exit from the function + of the OCI. In other words, the callback will be called after the execution + of the OCI function. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

UCBTYPE_REPLACE
+
+ This constant is used to register callbacks that completely replace the + call to the OCI function. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+

version
+
+ String constant stating the version of the module. Currently '4.3.1'. + +

+NOTE: This attribute is an extension to the DB API definition. +

+ +

+ +

+ + + + diff --git a/html/node5.html b/html/node5.html new file mode 100644 index 0000000..77c90a5 --- /dev/null +++ b/html/node5.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + +1.2 Exceptions + + + + + +

+1.2 Exceptions +

+ +

+

Warning
+
+ Exception raised for important warnings and defined by the DB API but not + actually used by cx_Oracle. +
+ +

+

Error
+
+ Exception that is the base class of all other exceptions defined by + cx_Oracle and is a subclass of the Python StandardError exception (defined in + the module exceptions). +
+ +

+

InterfaceError
+
+ Exception raised for errors that are related to the database interface rather + than the database itself. It is a subclass of Error. +
+ +

+

DatabaseError
+
+ Exception raised for errors that are related to the database. It is a + subclass of Error. +
+ +

+

DataError
+
+ Exception raised for errors that are due to problems with the processed data. + It is a subclass of DatabaseError. +
+ +

+

OperationalError
+
+ Exception raised for errors that are related to the operation of the database + but are not necessarily under the control of the progammer. It is a + subclass of DatabaseError. +
+ +

+

IntegrityError
+
+ Exception raised when the relational integrity of the database is affected. + It is a subclass of DatabaseError. +
+ +

+

InternalError
+
+ Exception raised when the database encounters an internal error. + It is a subclass of DatabaseError. +
+ +

+

ProgrammingError
+
+ Exception raised for programming errors. It is a subclass of DatabaseError. +
+ +

+

NotSupportedError
+
+ Exception raised when a method or database API was used which is not + supported by the database. It is a subclass of DatabaseError. +
+ +

+ +

+ + + + diff --git a/html/previous.png b/html/previous.png new file mode 100644 index 0000000000000000000000000000000000000000..497def42a0cf47e012bcd69599c2d3c405c3bca4 GIT binary patch literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsG5JzX3_DsEk!wRyY zCEWY>_i=7wnwQ?Thv7!lk3Q3b_hn3_6cyBDCqA^@uz$wKgFRYIje)6~-oBPwCCtL8 zRn{-bS5|{;$vbCgx;QBrpAU_J-m#4jD3wv4E??ROxPGE zh;n@Sv(s`4P`fxtdq843*h$7_s|?I8nO!hrVBng?_|v`ouqaRogQu&X%Q~loCID!* Bwsrsj literal 0 HcmV?d00001 diff --git a/html/sesspool.html b/html/sesspool.html new file mode 100644 index 0000000..0351471 --- /dev/null +++ b/html/sesspool.html @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + +5. SessionPool Objects + + + + + +

+
+5. SessionPool Objects +

+ +

+NOTE: This object is an extension the DB API and is only available in +Oracle 9i. + +

+

+ +
acquire()
+
+ Acquire a connection from the session pool and return a connection + object (2). +
+ +

+

busy
+
+ This read-only attribute returns the number of sessions currently acquired. +
+ +

+

+ +
drop(connection)
+
+ Drop the connection from the pool which is useful if the connection is no + longer usable (such as when the session is killed). +
+ +

+

dsn
+
+ This read-only attribute returns the TNS entry of the database to which a + connection has been established. +
+ +

+

increment
+
+ This read-only attribute returns the number of sessions that will be + established when additional sessions need to be created. +
+ +

+

max
+
+ This read-only attribute returns the maximum number of sessions that the + session pool can control. +
+ +

+

min
+
+ This read-only attribute returns the number of sessions with which the + session pool was created and the minimum number of sessions that will be + controlled by the session pool. +
+ +

+

name
+
+ This read-only attribute returns the name assigned to the session pool by + Oracle. +
+ +

+

opened
+
+ This read-only attribute returns the number of sessions currently opened by + the session pool. +
+ +

+

password
+
+ This read-only attribute returns the password of the user which established + the connection to the database. +
+ +

+

+ +
release(connection)
+
+ Release the connection back to the pool. This will be done automatically as + well if the connection object is garbage collected. +
+ +

+

timeout
+
+ This read-write attribute indicates the time (in seconds) after which idle + sessions will be terminated in order to maintain an optimum number of open + sessions. +
+ +

+

tnsentry
+
+ This read-only attribute returns the TNS entry of the database to which a + connection has been established. +
+ +

+

username
+
+ This read-only attribute returns the name of the user which established the + connection to the database. +
+ +

+ +

+ + + + diff --git a/html/up.png b/html/up.png new file mode 100644 index 0000000000000000000000000000000000000000..a90e0284436f3823439ae6c00a5fdd692a0663ff GIT binary patch literal 577 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsFUJzX3_DsI(Ix-HhKz~M64NIU=IfBR~Y$X4|dxhzrZ1U2!`f+hZ^ zm&+UyX=thNZFqZ3$(*HZ)#JUq4DD;ALk>J%yo5m@;6#0cY2xz7{28yk-^xCS`nzWO zo266P9IBt@>~+1oES71*pOcQcJRLiv0-~2c`um6}H-$A|S=%gM7riSdo=lopdpYOb zOw|LQ!vq)gGckPg+5M@o!H)g&n<$?iejfqORSc|K{2H#e@a5>fFx*hi?#O5&Ec=1Q zmZ^v}f$4(1dGdiQHI;%r$1^TG)Y&BLaGk%ULbzhlI&Nd22YsqSB1(eu(@M${i&7aF zDsB~D2)Op;jIWRXS6`p=KIg&=E*f7l4%)Egin+m6bAu2My;C}RD@#tS)zLfYbIn`# znzyFziSybQz0aLzW%%MP)bQo*kvO2a#$a@ip=GH1RMs z+iK#o&Bw&V#KXj3tBH{z6GLwqhx7L6v&}&5;vnq-iRoY`8Jn##FuP=S!Hj`{YZl{A T_wvJ{KqU;Gu6{1-oD!M + + + + + + + + + + + + + +4. Variable Objects + + + + + +

+
+4. Variable Objects +

+ +

+NOTE: The DB API definition does not define this object. + +

+

allocelems
+
+ This read-only attribute returns the number of elements allocated in an + array, or the number of scalar items that can be fetched into the variable. +
+ +

+

+ +
getvalue([pos=0])
+
+ Return the value at the given position in the variable. +
+ +

+

maxlength
+
+ This read-only attribute returns the maximum length of the variable. +
+ +

+

+ +
setvalue(pos, value)
+
+ Set the value at the given position in the variable. +
+ +

+ +

+ + + + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..f0f93fa --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[bdist_rpm] +doc_files = LICENSE.txt README.txt HISTORY.txt html test + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b1d36e3 --- /dev/null +++ b/setup.py @@ -0,0 +1,114 @@ +"""Distutils script for cx_Oracle. + +Windows platforms: + python setup.py build --compiler=mingw32 install + +Unix platforms + python setup.py build install + +""" + +import os +import sys + +from distutils.core import setup +from distutils.extension import Extension + +# define build constants +BUILD_VERSION = "4.3.1" + +# define the list of files to be included as documentation for Windows +dataFiles = None +if sys.platform in ("win32", "cygwin"): + baseName = "cx_Oracle-doc" + dataFiles = [ (baseName, [ "LICENSE.TXT", "README.TXT", "HISTORY.txt"]) ] + allFiles = [] + for fileName in open("MANIFEST").readlines(): + allFiles.append(fileName.strip()) + for dir in ("html", "test"): + files = [] + for name in allFiles: + if name.startswith(dir): + files.append(name) + dataFiles.append( ("%s/%s" % (baseName, dir), files) ) + +# define some variables +oracleHome = os.environ["ORACLE_HOME"] +if sys.platform == "win32": + libDirs = None + includeDirs = [os.path.join(oracleHome, "oci", "include"), \ + os.path.join(oracleHome, "rdbms", "demo")] + libs = [] +elif sys.platform == "cygwin": + includeDirs = ["/usr/include", "rdbms/demo", "rdbms/public", \ + "network/public", "oci/include"] + libDirs = ["bin", "lib"] + for i in range(len(includeDirs)): + includeDirs[i] = os.path.join(oracleHome, includeDirs[i]) + for i in range(len(libDirs)): + libDirs[i] = os.path.join(oracleHome, libDirs[i]) + libs = ["oci"] +else: + includeDirs = ["rdbms/demo", "rdbms/public", "network/public", + "sdk/include"] + if sys.platform == "darwin": + includeDirs.append("plsql/public") + for i in range(len(includeDirs)): + includeDirs[i] = os.path.join(oracleHome, includeDirs[i]) + libPath = os.path.join(oracleHome, "lib") + if sys.platform == "sunos5" and sys.maxint > 2147483647: + libPath = os.path.join(oracleHome, "lib64") + libDirs = [libPath, oracleHome] + libs = ["clntsh"] + +# NOTE: on HP-UX Itanium with Oracle 10g you need to add the library "ttsh10" +# to the list of libraries along with "clntsh"; since I am unable to test, I'll +# leave this as a comment until someone can verify when this is required +# without making other cases where sys.platform == "hp-ux11" stop working + +# setup extra link and compile args +extraCompileArgs = ["-DBUILD_VERSION=%s" % BUILD_VERSION] +extraLinkArgs = [] +if sys.platform == "aix4": + extraCompileArgs.append("-qcpluscmt") +elif sys.platform == "cygwin": + extraCompileArgs.append("-mno-cygwin") +elif sys.platform == "darwin": + extraLinkArgs = None +elif sys.platform == "win32": + import win32api + extraLinkArgs.append(win32api.GetModuleFileName(sys.dllhandle)) + extraLinkArgs.append(os.path.join(oracleHome, "bin", "oci.dll")) + +# force the inclusion of an RPATH linker directive if desired; this will +# eliminate the need for setting LD_LIBRARY_PATH but it also means that this +# location will be the only location searched for the Oracle client library +if "FORCE_RPATH" in os.environ: + extraLinkArgs.append("-Wl,-rpath,%s/lib" % oracleHome) + +# setup the extension +extension = Extension( + name = "cx_Oracle", + include_dirs = includeDirs, + libraries = libs, + library_dirs = libDirs, + extra_compile_args = extraCompileArgs, + extra_link_args = extraLinkArgs, + sources = ["cx_Oracle.c"]) + +# perform the setup +setup( + name = "cx_Oracle", + version = BUILD_VERSION, + description = "Python interface to Oracle", + license = "See LICENSE.txt", + data_files = dataFiles, + long_description = \ + "Python interface to Oracle conforming to the Python DB API 2.0 " + "specification.\n" + "See http://www.python.org/topics/database/DatabaseAPI-2.0.html.", + author = "Anthony Tuininga", + author_email = "anthony.tuininga@gmail.com", + url = "http://starship.python.net/crew/atuining", + ext_modules = [extension]) + diff --git a/test/Connection.py b/test/Connection.py new file mode 100644 index 0000000..439e876 --- /dev/null +++ b/test/Connection.py @@ -0,0 +1,116 @@ +"""Module for testing connections.""" + +import threading + +class TestConnection(TestCase): + + def __ConnectAndDrop(self): + """Connect to the database, perform a query and drop the connection.""" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry, threaded = True) + cursor = connection.cursor() + cursor.execute("select count(*) from TestNumbers") + count, = cursor.fetchone() + self.failUnlessEqual(count, 10) + + def setUp(self): + self.username = USERNAME + self.password = PASSWORD + self.tnsentry = TNSENTRY + + def verifyArgs(self, connection): + self.failUnlessEqual(connection.username, self.username, + "user name differs") + self.failUnlessEqual(connection.password, self.password, + "password differs") + self.failUnlessEqual(connection.tnsentry, self.tnsentry, + "tnsentry differs") + + def testAllArgs(self): + "connection to database with user, password, TNS separate" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + self.verifyArgs(connection) + + def testBadConnectString(self): + "connection to database with bad connect string" + self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect, + self.username) + self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect, + self.username + "@" + self.tnsentry) + self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect, + self.username + "@" + self.tnsentry + "/" + self.password) + + def testBadPassword(self): + "connection to database with bad password" + self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect, + self.username, self.password + "X", self.tnsentry) + + def testExceptionOnClose(self): + "confirm an exception is raised after closing a connection" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + connection.close() + self.failUnlessRaises(cx_Oracle.InterfaceError, connection.rollback) + + def testMakeDSN(self): + "test making a data source name from host, port and sid" + formatString = "(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)" + \ + "(HOST=%s)(PORT=%d)))(CONNECT_DATA=(SID=%s)))" + args = ("hostname", 1521, "TEST") + result = cx_Oracle.makedsn(*args) + self.failUnlessEqual(result, formatString % args) + + def testSingleArg(self): + "connection to database with user, password, TNS together" + connection = cx_Oracle.connect("%s/%s@%s" % \ + (self.username, self.password, self.tnsentry)) + self.verifyArgs(connection) + + def testVersion(self): + "connection version is a string" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + self.failUnless(isinstance(connection.version, str)) + + def testRollbackOnClose(self): + "connection rolls back before close" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + cursor = connection.cursor() + cursor.execute("truncate table TestExecuteMany") + otherConnection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + otherCursor = otherConnection.cursor() + otherCursor.execute("insert into TestExecuteMany values (1)") + otherConnection.close() + cursor.execute("select count(*) from TestExecuteMany") + count, = cursor.fetchone() + self.failUnlessEqual(count, 0) + + def testRollbackOnDel(self): + "connection rolls back before destruction" + connection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + cursor = connection.cursor() + cursor.execute("truncate table TestExecuteMany") + otherConnection = cx_Oracle.connect(self.username, self.password, + self.tnsentry) + otherCursor = otherConnection.cursor() + otherCursor.execute("insert into TestExecuteMany values (1)") + del otherCursor + del otherConnection + cursor.execute("select count(*) from TestExecuteMany") + count, = cursor.fetchone() + self.failUnlessEqual(count, 0) + + def testThreading(self): + "connection to database with multiple threads" + threads = [] + for i in range(20): + thread = threading.Thread(None, self.__ConnectAndDrop) + threads.append(thread) + thread.start() + for thread in threads: + thread.join() + diff --git a/test/Cursor.py b/test/Cursor.py new file mode 100644 index 0000000..32f5fd5 --- /dev/null +++ b/test/Cursor.py @@ -0,0 +1,225 @@ +"""Module for testing cursor objects.""" + +import cx_Oracle + +class TestCursor(BaseTestCase): + + def testExecuteNoArgs(self): + """test executing a statement without any arguments""" + result = self.cursor.execute("begin null; end;") + self.failUnlessEqual(result, None) + + def testExecuteNoStatementWithArgs(self): + """test executing a None statement with bind variables""" + self.failUnlessRaises(cx_Oracle.ProgrammingError, self.cursor.execute, + None, x = 5) + + def testExecuteEmptyKeywordArgs(self): + """test executing a statement with args and empty keyword args""" + simpleVar = self.cursor.var(cx_Oracle.NUMBER) + args = [simpleVar] + kwArgs = {} + result = self.cursor.execute("begin :1 := 25; end;", args, **kwArgs) + self.failUnlessEqual(result, None) + self.failUnlessEqual(simpleVar.getvalue(), 25) + + def testExecuteKeywordArgs(self): + """test executing a statement with keyword arguments""" + simpleVar = self.cursor.var(cx_Oracle.NUMBER) + result = self.cursor.execute("begin :p_Value := 5; end;", + p_Value = simpleVar) + self.failUnlessEqual(result, None) + self.failUnlessEqual(simpleVar.getvalue(), 5) + + def testExecuteDictionaryArg(self): + """test executing a statement with a dictionary argument""" + simpleVar = self.cursor.var(cx_Oracle.NUMBER) + dictArg = { "p_Value" : simpleVar } + result = self.cursor.execute("begin :p_Value := 10; end;", dictArg) + self.failUnlessEqual(result, None) + self.failUnlessEqual(simpleVar.getvalue(), 10) + + def testExecuteMultipleMethod(self): + """test executing a statement with both a dict arg and keyword args""" + simpleVar = self.cursor.var(cx_Oracle.NUMBER) + dictArg = { "p_Value" : simpleVar } + self.failUnlessRaises(cx_Oracle.InterfaceError, self.cursor.execute, + "begin :p_Value := 15; end;", dictArg, p_Value = simpleVar) + + def testExecuteAndModifyArraySize(self): + """test executing a statement and then changing the array size""" + self.cursor.execute("select IntCol from TestNumbers") + self.cursor.arraysize = 20 + self.failUnlessEqual(len(self.cursor.fetchall()), 10) + + def testCallProc(self): + """test executing a stored procedure""" + var = self.cursor.var(cx_Oracle.NUMBER) + results = self.cursor.callproc("proc_Test", ("hi", 5, var)) + self.failUnlessEqual(results, ["hi", 10, 2.0]) + + def testCallProcNoArgs(self): + """test executing a stored procedure without any arguments""" + results = self.cursor.callproc("proc_TestNoArgs") + self.failUnlessEqual(results, []) + + def testCallFunc(self): + """test executing a stored function""" + results = self.cursor.callfunc("func_Test", cx_Oracle.NUMBER, + ("hi", 5)) + self.failUnlessEqual(results, 7) + + def testCallFuncNoArgs(self): + """test executing a stored function without any arguments""" + results = self.cursor.callfunc("func_TestNoArgs", cx_Oracle.NUMBER) + self.failUnlessEqual(results, 712) + + def testExecuteManyByName(self): + """test executing a statement multiple times (named args)""" + self.cursor.execute("truncate table TestExecuteMany") + rows = [ { "value" : n } for n in range(250) ] + self.cursor.arraysize = 100 + statement = "insert into TestExecuteMany values (:value)" + self.cursor.executemany(statement, rows) + self.connection.commit() + self.cursor.execute("select count(*) from TestExecuteMany") + count, = self.cursor.fetchone() + self.failUnlessEqual(count, len(rows)) + + def testExecuteManyByPosition(self): + """test executing a statement multiple times (positional args)""" + self.cursor.execute("truncate table TestExecuteMany") + rows = [ [n] for n in range(230) ] + self.cursor.arraysize = 100 + statement = "insert into TestExecuteMany values (:1)" + self.cursor.executemany(statement, rows) + self.connection.commit() + self.cursor.execute("select count(*) from TestExecuteMany") + count, = self.cursor.fetchone() + self.failUnlessEqual(count, len(rows)) + + def testExecuteManyWithPrepare(self): + """test executing a statement multiple times (with prepare)""" + self.cursor.execute("truncate table TestExecuteMany") + rows = [ [n] for n in range(225) ] + self.cursor.arraysize = 100 + statement = "insert into TestExecuteMany values (:1)" + self.cursor.prepare(statement) + self.cursor.executemany(None, rows) + self.connection.commit() + self.cursor.execute("select count(*) from TestExecuteMany") + count, = self.cursor.fetchone() + self.failUnlessEqual(count, len(rows)) + + def testExecuteManyWithRebind(self): + """test executing a statement multiple times (with rebind)""" + self.cursor.execute("truncate table TestExecuteMany") + rows = [ [n] for n in range(235) ] + self.cursor.arraysize = 100 + statement = "insert into TestExecuteMany values (:1)" + self.cursor.executemany(statement, rows[:50]) + self.cursor.executemany(statement, rows[50:]) + self.connection.commit() + self.cursor.execute("select count(*) from TestExecuteMany") + count, = self.cursor.fetchone() + self.failUnlessEqual(count, len(rows)) + + def testExecuteManyWithExecption(self): + """test executing a statement multiple times (with exception)""" + self.cursor.execute("truncate table TestExecuteMany") + rows = [ { "p_Value" : n } for n in (1, 2, 3, 2, 5) ] + statement = "insert into TestExecuteMany values (:p_Value)" + self.failUnlessRaises(cx_Oracle.DatabaseError, self.cursor.executemany, + statement, rows) + self.failUnlessEqual(self.cursor.rowcount, 3) + + def testPrepare(self): + """test preparing a statement and executing it multiple times""" + self.failUnlessEqual(self.cursor.statement, None) + statement = "begin :p_Value := :p_Value + 5; end;" + self.cursor.prepare(statement) + var = self.cursor.var(cx_Oracle.NUMBER) + self.failUnlessEqual(self.cursor.statement, statement) + var.setvalue(0, 2) + self.cursor.execute(None, p_Value = var) + self.failUnlessEqual(var.getvalue(), 7) + self.cursor.execute(None, p_Value = var) + self.failUnlessEqual(var.getvalue(), 12) + self.cursor.execute("begin :p_Value2 := 3; end;", p_Value2 = var) + self.failUnlessEqual(var.getvalue(), 3) + + def testExceptionOnClose(self): + "confirm an exception is raised after closing a cursor" + self.cursor.close() + self.failUnlessRaises(cx_Oracle.InterfaceError, self.cursor.execute, + "select 1 from dual") + + def testIterators(self): + """test iterators""" + self.cursor.execute(""" + select IntCol + from TestNumbers + where IntCol between 1 and 3 + order by IntCol""") + rows = [] + for row in self.cursor: + rows.append(row[0]) + self.failUnlessEqual(rows, [1, 2, 3]) + + def testIteratorsInterrupted(self): + """test iterators (with intermediate execute)""" + self.cursor.execute("truncate table TestExecuteMany") + self.cursor.execute(""" + select IntCol + from TestNumbers + where IntCol between 1 and 3 + order by IntCol""") + testIter = iter(self.cursor) + value, = testIter.next() + self.cursor.execute("insert into TestExecuteMany values (1)") + self.failUnlessRaises(cx_Oracle.InterfaceError, testIter.next) + + def testBindNames(self): + """test that bindnames() works correctly.""" + self.failUnlessRaises(cx_Oracle.ProgrammingError, + self.cursor.bindnames) + self.cursor.prepare("begin null; end;") + self.failUnlessEqual(self.cursor.bindnames(), []) + self.cursor.prepare("begin :retval := :inval + 5; end;") + self.failUnlessEqual(self.cursor.bindnames(), ["RETVAL", "INVAL"]) + self.cursor.prepare("begin :retval := :a * :a + :b * :b; end;") + self.failUnlessEqual(self.cursor.bindnames(), ["RETVAL", "A", "B"]) + self.cursor.prepare("begin :a := :b + :c + :d + :e + :f + :g + " + \ + ":h + :i + :j + :k + :l; end;") + self.failUnlessEqual(self.cursor.bindnames(), + ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]) + + def testBadPrepare(self): + """test that subsequent executes succeed after bad prepare""" + self.failUnlessRaises(cx_Oracle.DatabaseError, + self.cursor.execute, + "begin raise_application_error(-20000, 'this); end;") + self.cursor.execute("begin null; end;") + + def testBadExecute(self): + """test that subsequent fetches fail after bad execute""" + self.failUnlessRaises(cx_Oracle.DatabaseError, + self.cursor.execute, "select y from dual") + self.failUnlessRaises(cx_Oracle.InterfaceError, + self.cursor.fetchall) + + def testSetInputSizesMultipleMethod(self): + """test setting input sizes with both positional and keyword args""" + self.failUnlessRaises(cx_Oracle.InterfaceError, + self.cursor.setinputsizes, 5, x = 5) + + def testSetInputSizesByPosition(self): + """test setting input sizes with positional args""" + var = self.cursor.var(cx_Oracle.STRING, 100) + self.cursor.setinputsizes(None, 5, None, 10, None, cx_Oracle.NUMBER) + self.cursor.execute(""" + begin + :1 := :2 || to_char(:3) || :4 || to_char(:5) || to_char(:6); + end;""", [var, 'test_', 5, '_second_', 3, 7]) + self.failUnlessEqual(var.getvalue(), "test_5_second_37") + diff --git a/test/CursorVar.py b/test/CursorVar.py new file mode 100644 index 0000000..3ac37e3 --- /dev/null +++ b/test/CursorVar.py @@ -0,0 +1,50 @@ +"""Module for testing cursor variables.""" + +import sys + +class TestCursorVar(BaseTestCase): + + def testBindCursor(self): + "test binding in a cursor" + cursor = self.connection.cursor() + self.failUnlessEqual(cursor.description, None) + self.cursor.execute(""" + begin + open :p_Cursor for select 1 NumberCol from dual; + end;""", + p_Cursor = cursor) + self.failUnlessEqual(cursor.description, + [ ('NUMBERCOL', cx_Oracle.NUMBER, 127, 2, 0, 0, 1) ]) + self.failUnlessEqual(cursor.fetchall(), [(1.0,)]) + + def testBindCursorInPackage(self): + "test binding in a cursor from a package" + cursor = self.connection.cursor() + self.failUnlessEqual(cursor.description, None) + self.cursor.callproc("pkg_TestOutCursors.TestOutCursor", (2, cursor)) + self.failUnlessEqual(cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('STRINGCOL', cx_Oracle.STRING, 20, 20, 0, 0, 0) ]) + self.failUnlessEqual(cursor.fetchall(), + [ (1, 'String 1'), (2, 'String 2') ]) + + def testFetchCursor(self): + "test fetching a cursor" + self.cursor.execute(""" + select + IntCol, + cursor(select IntCol + 1 from dual) CursorValue + from TestNumbers + order by IntCol""") + if len(str(sys.maxint)) == 10: + size = 4 + else: + size = 8 + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('CURSORVALUE', cx_Oracle.CURSOR, -1, size, 0, 0, 1) ]) + for i in range(1, 11): + number, cursor = self.cursor.fetchone() + self.failUnlessEqual(number, i) + self.failUnlessEqual(cursor.fetchall(), [(i + 1,)]) + diff --git a/test/DateTimeVar.py b/test/DateTimeVar.py new file mode 100644 index 0000000..725a933 --- /dev/null +++ b/test/DateTimeVar.py @@ -0,0 +1,230 @@ +"""Module for testing date/time variables.""" + +import sys +import time + +class TestDateTimeVar(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + self.rawData = [] + self.dataByKey = {} + for i in range(1, 11): + timeTuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1) + timeInTicks = time.mktime(timeTuple) + i * 86400 + i * 8640 + dateCol = cx_Oracle.TimestampFromTicks(int(timeInTicks)) + if i % 2: + timeInTicks = time.mktime(timeTuple) + i * 86400 * 2 + \ + i * 12960 + nullableCol = cx_Oracle.TimestampFromTicks(int(timeInTicks)) + else: + nullableCol = None + tuple = (i, dateCol, nullableCol) + self.rawData.append(tuple) + self.dataByKey[i] = tuple + + def testBindDate(self): + "test binding in a date" + self.cursor.execute(""" + select * from TestDates + where DateCol = :p_Value""", + p_Value = cx_Oracle.Timestamp(2002, 12, 13, 9, 36, 0)) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[4]]) + + if sys.version_info[:2] != (2, 2): + def testBindDateTime(self): + "test binding in a Python 2.3 and higher date time" + import datetime + self.cursor.execute(""" + select * from TestDates + where DateCol = :value""", + value = datetime.datetime(2002, 12, 13, 9, 36, 0)) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[4]]) + + def testBindDateAfterString(self): + "test binding in a date after setting input sizes to a string" + self.cursor.setinputsizes(p_Value = 15) + self.cursor.execute(""" + select * from TestDates + where DateCol = :p_Value""", + p_Value = cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0)) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[5]]) + + def testBindNull(self): + "test binding in a null" + self.cursor.setinputsizes(p_Value = cx_Oracle.DATETIME) + self.cursor.execute(""" + select * from TestDates + where DateCol = :p_Value""", + p_Value = None) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testBindDateArrayDirect(self): + "test binding in a date array" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = [r[1] for r in self.rawData] + statement = """ + begin + :p_ReturnValue := pkg_TestDateArrays.TestInArrays( + :p_StartValue, :p_BaseDate, :p_Array); + end;""" + self.cursor.execute(statement, + p_ReturnValue = returnValue, + p_StartValue = 5, + p_BaseDate = cx_Oracle.Date(2002, 12, 12), + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 35.5) + array = array + array[:5] + self.cursor.execute(statement, + p_StartValue = 7, + p_BaseDate = cx_Oracle.Date(2002, 12, 13), + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 24.0) + + def testBindDateArrayBySizes(self): + "test binding in a date array (with setinputsizes)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + self.cursor.setinputsizes(p_Array = [cx_Oracle.DATETIME, 10]) + array = [r[1] for r in self.rawData] + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestDateArrays.TestInArrays( + :p_StartValue, :p_BaseDate, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_StartValue = 6, + p_BaseDate = cx_Oracle.Date(2002, 12, 13), + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 26.5) + + def testBindDateArrayByVar(self): + "test binding in a date array (with arrayvar)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = self.cursor.arrayvar(cx_Oracle.DATETIME, 10, 20) + array.setvalue(0, [r[1] for r in self.rawData]) + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestDateArrays.TestInArrays( + :p_StartValue, :p_BaseDate, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_StartValue = 7, + p_BaseDate = cx_Oracle.Date(2002, 12, 14), + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 17.5) + + def testBindInOutDateArrayByVar(self): + "test binding in/out a date array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.DATETIME, 10, 100) + originalData = [r[1] for r in self.rawData] + array.setvalue(0, originalData) + self.cursor.execute(""" + begin + pkg_TestDateArrays.TestInOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 5, + p_Array = array) + self.failUnlessEqual(array.getvalue(), + [ cx_Oracle.Timestamp(2002, 12, 17, 2, 24, 0), + cx_Oracle.Timestamp(2002, 12, 18, 4, 48, 0), + cx_Oracle.Timestamp(2002, 12, 19, 7, 12, 0), + cx_Oracle.Timestamp(2002, 12, 20, 9, 36, 0), + cx_Oracle.Timestamp(2002, 12, 21, 12, 0, 0) ] + \ + originalData[5:]) + + def testBindOutDateArrayByVar(self): + "test binding out a date array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.DATETIME, 6, 100) + self.cursor.execute(""" + begin + pkg_TestDateArrays.TestOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 6, + p_Array = array) + self.failUnlessEqual(array.getvalue(), + [ cx_Oracle.Timestamp(2002, 12, 13, 4, 48, 0), + cx_Oracle.Timestamp(2002, 12, 14, 9, 36, 0), + cx_Oracle.Timestamp(2002, 12, 15, 14, 24, 0), + cx_Oracle.Timestamp(2002, 12, 16, 19, 12, 0), + cx_Oracle.Timestamp(2002, 12, 18, 0, 0, 0), + cx_Oracle.Timestamp(2002, 12, 19, 4, 48, 0) ]) + + def testBindOutSetInputSizes(self): + "test binding out with set input sizes defined" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.DATETIME) + self.cursor.execute(""" + begin + :p_Value := to_date(20021209, 'YYYYMMDD'); + end;""") + self.failUnlessEqual(vars["p_Value"].getvalue(), + cx_Oracle.Timestamp(2002, 12, 9)) + + def testBindInOutSetInputSizes(self): + "test binding in/out with set input sizes defined" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.DATETIME) + self.cursor.execute(""" + begin + :p_Value := :p_Value + 5.25; + end;""", + p_Value = cx_Oracle.Timestamp(2002, 12, 12, 10, 0, 0)) + self.failUnlessEqual(vars["p_Value"].getvalue(), + cx_Oracle.Timestamp(2002, 12, 17, 16, 0, 0)) + + def testBindOutVar(self): + "test binding out with cursor.var() method" + var = self.cursor.var(cx_Oracle.DATETIME) + self.cursor.execute(""" + begin + :p_Value := to_date('20021231 12:31:00', + 'YYYYMMDD HH24:MI:SS'); + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), + cx_Oracle.Timestamp(2002, 12, 31, 12, 31, 0)) + + def testBindInOutVarDirectSet(self): + "test binding in/out with cursor.var() method" + var = self.cursor.var(cx_Oracle.DATETIME) + var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0)) + self.cursor.execute(""" + begin + :p_Value := :p_Value + 5.25; + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), + cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0)) + + def testCursorDescription(self): + "test cursor description is accurate" + self.cursor.execute("select * from TestDates") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('DATECOL', cx_Oracle.DATETIME, 23, 7, 0, 0, 0), + ('NULLABLECOL', cx_Oracle.DATETIME, 23, 7, 0, 0, 1) ]) + + def testFetchAll(self): + "test that fetching all of the data returns the correct results" + self.cursor.execute("select * From TestDates order by IntCol") + self.failUnlessEqual(self.cursor.fetchall(), self.rawData) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testFetchMany(self): + "test that fetching data in chunks returns the correct results" + self.cursor.execute("select * From TestDates order by IntCol") + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[0:3]) + self.failUnlessEqual(self.cursor.fetchmany(2), self.rawData[3:5]) + self.failUnlessEqual(self.cursor.fetchmany(4), self.rawData[5:9]) + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[9:]) + self.failUnlessEqual(self.cursor.fetchmany(3), []) + + def testFetchOne(self): + "test that fetching a single row returns the correct results" + self.cursor.execute(""" + select * + from TestDates + where IntCol in (3, 4) + order by IntCol""") + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[3]) + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[4]) + self.failUnlessEqual(self.cursor.fetchone(), None) + diff --git a/test/LobVar.py b/test/LobVar.py new file mode 100644 index 0000000..a05af22 --- /dev/null +++ b/test/LobVar.py @@ -0,0 +1,123 @@ +"""Module for testing LOB (CLOB and BLOB) variables.""" + +class TestLobVar(BaseTestCase): + + def __PerformTest(self, type, inputType): + longString = "" + directType = getattr(cx_Oracle, type) + self.cursor.execute("truncate table Test%ss" % type) + for i in range(0, 11): + if i > 0: + char = chr(ord('A') + i - 1) + longString += char * 25000 + elif inputType != directType: + continue + self.cursor.setinputsizes(longString = inputType) + self.cursor.execute(""" + insert into Test%ss ( + IntCol, + %sCol + ) values ( + :integerValue, + :longString + )""" % (type, type), + integerValue = i, + longString = longString) + self.connection.commit() + self.cursor.execute(""" + select * + from Test%ss + order by IntCol""" % type) + longString = "" + for row in self.cursor: + integerValue, lob = row + if integerValue == 0: + self.failUnlessEqual(lob.size(), 0) + self.failUnlessEqual(lob.read(), "") + else: + char = chr(ord('A') + integerValue - 1) + prevChar = chr(ord('A') + integerValue - 2) + longString += char * 25000 + self.failUnlessEqual(lob.size(), len(longString)) + self.failUnlessEqual(lob.read(), longString) + self.failUnlessEqual(str(lob), longString) + self.failUnlessEqual(lob.read(len(longString)), char) + if integerValue > 1: + offset = (integerValue - 1) * 25000 - 4 + string = prevChar * 5 + char * 5 + self.failUnlessEqual(lob.read(offset, 10), string) + + def __TestTrim(self, type): + self.cursor.execute("truncate table Test%ss" % type) + self.cursor.setinputsizes(longString = getattr(cx_Oracle, type)) + self.cursor.execute(""" + insert into Test%ss ( + IntCol, + %sCol + ) values ( + :integerValue, + :longString + )""" % (type, type), + integerValue = 1, + longString = "X" * 75000) + self.cursor.execute(""" + select %sCol + from Test%ss + where IntCol = 1""" % (type, type)) + lob, = self.cursor.fetchone() + self.failUnlessEqual(lob.size(), 75000) + lob.trim(25000) + self.failUnlessEqual(lob.size(), 25000) + lob.trim() + self.failUnlessEqual(lob.size(), 0) + + def testBLOBsIndirect(self): + "test binding and fetching BLOB data (indirectly)" + self.__PerformTest("BLOB", cx_Oracle.LONG_BINARY) + + def testCLOBsIndirect(self): + "test binding and fetching CLOB data (indirectly)" + self.__PerformTest("CLOB", cx_Oracle.LONG_STRING) + + def testBLOBsDirect(self): + "test binding and fetching BLOB data (directly)" + self.__PerformTest("BLOB", cx_Oracle.BLOB) + + def testCLOBsDirect(self): + "test binding and fetching CLOB data (directly)" + self.__PerformTest("CLOB", cx_Oracle.CLOB) + + def testCLOBCursorDescription(self): + "test cursor description is accurate for CLOBs" + self.cursor.execute("select * from TestCLOBs") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('CLOBCOL', cx_Oracle.CLOB, -1, 4000, 0, 0, 0) ]) + + def testBLOBCursorDescription(self): + "test cursor description is accurate for BLOBs" + self.cursor.execute("select * from TestBLOBs") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('BLOBCOL', cx_Oracle.BLOB, -1, 4000, 0, 0, 0) ]) + + def testBLOBTrim(self): + "test trimming a BLOB" + self.__TestTrim("BLOB") + + def testCLOBTrim(self): + "test trimming a CLOB" + self.__TestTrim("CLOB") + + def testMultipleFetch(self): + "test retrieving data from a CLOB after multiple fetches" + self.cursor.arraysize = 1 + self.cursor.execute("select CLOBCol from TestCLOBS") + rows = self.cursor.fetchall() + self.failUnlessRaises(cx_Oracle.ProgrammingError, rows[1][0].read) + + +if __name__ == "__main__": + print "Testing cx_Oracle version", cx_Oracle.version + unittest.main() + diff --git a/test/LongVar.py b/test/LongVar.py new file mode 100644 index 0000000..c21dbd7 --- /dev/null +++ b/test/LongVar.py @@ -0,0 +1,91 @@ +"""Module for testing long and long raw variables.""" + +class TestLongVar(BaseTestCase): + + def __PerformTest(self, a_Type, a_InputType): + self.cursor.execute("truncate table Test%ss" % a_Type) + longString = "" + for i in range(1, 11): + char = chr(ord('A') + i - 1) + longString += char * 25000 + self.cursor.setinputsizes(p_LongString = a_InputType) + self.cursor.execute(""" + insert into Test%ss ( + IntCol, + %sCol + ) values ( + :p_IntegerValue, + :p_LongString + )""" % (a_Type, a_Type), + p_IntegerValue = i, + p_LongString = longString) + self.connection.commit() + self.cursor.setoutputsize(250000, 2) + self.cursor.execute(""" + select * + from Test%ss + order by IntCol""" % a_Type) + longString = "" + while 1: + row = self.cursor.fetchone() + if row is None: + break + integerValue, fetchedValue = row + char = chr(ord('A') + integerValue - 1) + longString += char * 25000 + self.failUnlessEqual(len(fetchedValue), integerValue * 25000) + self.failUnlessEqual(fetchedValue, longString) + + def testLongs(self): + "test binding and fetching long data" + self.__PerformTest("Long", cx_Oracle.LONG_STRING) + + def testLongRaws(self): + "test binding and fetching long raw data" + self.__PerformTest("LongRaw", cx_Oracle.LONG_BINARY) + + def testLongCursorDescription(self): + "test cursor description is accurate for longs" + self.cursor.execute("select * from TestLongs") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('LONGCOL', cx_Oracle.LONG_STRING, -1, 0, 0, 0, 0) ]) + + def testLongRawCursorDescription(self): + "test cursor description is accurate for long raws" + self.cursor.execute("select * from TestLongRaws") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('LONGRAWCOL', cx_Oracle.LONG_BINARY, -1, 0, 0, 0, 0) ]) + + def testSetOutputSizesAll(self): + "test setoutputsizes is valid (all)" + self.cursor.setoutputsize(25000) + longVar = self.cursor.execute("select * from TestLongRaws")[1] + self.failUnlessEqual(longVar.maxlength, + 25004 * self.connection.maxBytesPerCharacter) + + def testSetOutputSizesWrongColumn(self): + "test setoutputsizes is valid (wrong column)" + self.cursor.setoutputsize(25000, 1) + longVar = self.cursor.execute("select * from TestLongRaws")[1] + self.failUnlessEqual(longVar.maxlength, + 131072 * self.connection.maxBytesPerCharacter) + + def testSetOutputSizesRightColumn(self): + "test setoutputsizes is valid (right column)" + self.cursor.setoutputsize(35000, 2) + longVar = self.cursor.execute("select * from TestLongRaws")[1] + self.failUnlessEqual(longVar.maxlength, + 35004 * self.connection.maxBytesPerCharacter) + + def testArraySizeTooLarge(self): + "test array size too large generates an exception" + self.cursor.arraysize = 65536 + self.failUnlessRaises(ValueError, self.cursor.execute, + "select * from TestLongRaws") + +if __name__ == "__main__": + print "Testing cx_Oracle version", cx_Oracle.version + unittest.main() + diff --git a/test/NumberVar.py b/test/NumberVar.py new file mode 100644 index 0000000..818f691 --- /dev/null +++ b/test/NumberVar.py @@ -0,0 +1,273 @@ +"""Module for testing number variables.""" + +import cx_Oracle +import sys + +class TestNumberVar(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + self.rawData = [] + self.dataByKey = {} + for i in range(1, 11): + numberCol = i + i * 0.25 + floatCol = i + i * 0.75 + unconstrainedCol = i ** 3 + i * 0.5 + if i % 2: + nullableCol = 143L ** i + else: + nullableCol = None + dataTuple = (i, numberCol, floatCol, unconstrainedCol, nullableCol) + self.rawData.append(dataTuple) + self.dataByKey[i] = dataTuple + + if sys.version_info[:2] > (2, 3): + def testBindDecimal(self): + "test binding in a decimal.Decimal" + import decimal + self.cursor.execute(""" + select * from TestNumbers + where NumberCol - :value1 - :value2 = trunc(NumberCol)""", + value1 = decimal.Decimal("0.20"), + value2 = decimal.Decimal("0.05")) + self.failUnlessEqual(self.cursor.fetchall(), + [self.dataByKey[1], self.dataByKey[5], self.dataByKey[9]]) + + def testBindFloat(self): + "test binding in a float" + self.cursor.execute(""" + select * from TestNumbers + where NumberCol - :value = trunc(NumberCol)""", + value = 0.25) + self.failUnlessEqual(self.cursor.fetchall(), + [self.dataByKey[1], self.dataByKey[5], self.dataByKey[9]]) + + def testBindInteger(self): + "test binding in an integer" + self.cursor.execute(""" + select * from TestNumbers + where IntCol = :value""", + value = 2) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[2]]) + + def testBindSmallLong(self): + "test binding in a small long integer" + self.cursor.execute(""" + select * from TestNumbers + where IntCol = :value""", + value = 3L) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[3]]) + + def testBindLargeLong(self): + "test binding in a large long integer" + valueVar = self.cursor.var(cx_Oracle.NUMBER) + valueVar.setvalue(0, 6088343244) + self.cursor.execute(""" + begin + :value := :value + 5; + end;""", + value = valueVar) + value = valueVar.getvalue() + self.failUnlessEqual(value, 6088343249) + + def testBindIntegerAfterString(self): + "test binding in an number after setting input sizes to a string" + self.cursor.setinputsizes(value = 15) + self.cursor.execute(""" + select * from TestNumbers + where IntCol = :value""", + value = 3) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[3]]) + + def testBindNull(self): + "test binding in a null" + self.cursor.execute(""" + select * from TestNumbers + where IntCol = :value""", + value = None) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testBindNumberArrayDirect(self): + "test binding in a number array" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = [r[1] for r in self.rawData] + statement = """ + begin + :p_ReturnValue := pkg_TestNumberArrays.TestInArrays( + :p_StartValue, :p_Array); + end;""" + self.cursor.execute(statement, + p_ReturnValue = returnValue, + p_StartValue = 5, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 73.75) + array = range(15) + self.cursor.execute(statement, + p_StartValue = 10, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 115.0) + + def testBindNumberArrayBySizes(self): + "test binding in a number array (with setinputsizes)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + self.cursor.setinputsizes(p_Array = [cx_Oracle.NUMBER, 10]) + array = [r[1] for r in self.rawData] + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestNumberArrays.TestInArrays( + :p_StartValue, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_StartValue = 6, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 74.75) + + def testBindNumberArrayByVar(self): + "test binding in a number array (with arrayvar)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = self.cursor.arrayvar(cx_Oracle.NUMBER, + [r[1] for r in self.rawData]) + array.setvalue(0, [r[1] for r in self.rawData]) + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestNumberArrays.TestInArrays( + :p_IntegerValue, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_IntegerValue = 7, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 75.75) + + def testBindZeroLengthNumberArrayByVar(self): + "test binding in a zero length number array (with arrayvar)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = self.cursor.arrayvar(cx_Oracle.NUMBER, 0) + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestNumberArrays.TestInArrays( + :p_IntegerValue, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_IntegerValue = 8, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 8.0) + self.failUnlessEqual(array.getvalue(), []) + + def testBindInOutNumberArrayByVar(self): + "test binding in/out a number array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.NUMBER, 10) + originalData = [r[1] for r in self.rawData] + expectedData = [originalData[i - 1] * 10 for i in range(1, 6)] + \ + originalData[5:] + array.setvalue(0, originalData) + self.cursor.execute(""" + begin + pkg_TestNumberArrays.TestInOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 5, + p_Array = array) + self.failUnlessEqual(array.getvalue(), expectedData) + + def testBindOutNumberArrayByVar(self): + "test binding out a Number array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.NUMBER, 6) + expectedData = [i * 100 for i in range(1, 7)] + self.cursor.execute(""" + begin + pkg_TestNumberArrays.TestOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 6, + p_Array = array) + self.failUnlessEqual(array.getvalue(), expectedData) + + def testBindOutSetInputSizes(self): + "test binding out with set input sizes defined" + vars = self.cursor.setinputsizes(value = cx_Oracle.NUMBER) + self.cursor.execute(""" + begin + :value := 5; + end;""") + self.failUnlessEqual(vars["value"].getvalue(), 5) + + def testBindInOutSetInputSizes(self): + "test binding in/out with set input sizes defined" + vars = self.cursor.setinputsizes(value = cx_Oracle.NUMBER) + self.cursor.execute(""" + begin + :value := :value + 5; + end;""", + value = 1.25) + self.failUnlessEqual(vars["value"].getvalue(), 6.25) + + def testBindOutVar(self): + "test binding out with cursor.var() method" + var = self.cursor.var(cx_Oracle.NUMBER) + self.cursor.execute(""" + begin + :value := 5; + end;""", + value = var) + self.failUnlessEqual(var.getvalue(), 5) + + def testBindInOutVarDirectSet(self): + "test binding in/out with cursor.var() method" + var = self.cursor.var(cx_Oracle.NUMBER) + var.setvalue(0, 2.25) + self.cursor.execute(""" + begin + :value := :value + 5; + end;""", + value = var) + self.failUnlessEqual(var.getvalue(), 7.25) + + def testCursorDescription(self): + "test cursor description is accurate" + self.cursor.execute("select * from TestNumbers") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('NUMBERCOL', cx_Oracle.NUMBER, 13, 22, 9, 2, 0), + ('FLOATCOL', cx_Oracle.NUMBER, 127, 22, 126, -127, 0), + ('UNCONSTRAINEDCOL', cx_Oracle.NUMBER, 127, 22, 0, -127, 0), + ('NULLABLECOL', cx_Oracle.NUMBER, 39, 22, 38, 0, 1) ]) + + def testFetchAll(self): + "test that fetching all of the data returns the correct results" + self.cursor.execute("select * From TestNumbers order by IntCol") + self.failUnlessEqual(self.cursor.fetchall(), self.rawData) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testFetchMany(self): + "test that fetching data in chunks returns the correct results" + self.cursor.execute("select * From TestNumbers order by IntCol") + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[0:3]) + self.failUnlessEqual(self.cursor.fetchmany(2), self.rawData[3:5]) + self.failUnlessEqual(self.cursor.fetchmany(4), self.rawData[5:9]) + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[9:]) + self.failUnlessEqual(self.cursor.fetchmany(3), []) + + def testFetchOne(self): + "test that fetching a single row returns the correct results" + self.cursor.execute(""" + select * + from TestNumbers + where IntCol in (3, 4) + order by IntCol""") + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[3]) + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[4]) + self.failUnlessEqual(self.cursor.fetchone(), None) + + def testReturnAsLong(self): + "test that fetching a long integer returns such in Python" + self.cursor.execute(""" + select NullableCol + from TestNumbers + where IntCol = 9""") + col, = self.cursor.fetchone() + self.failUnless(isinstance(col, long), "long integer not returned") + + def testReturnAsFloat(self): + "test that fetching a floating point number returns such in Python" + self.cursor.execute("select 1.25 from dual") + result, = self.cursor.fetchone() + self.failUnlessEqual(result, 1.25) + diff --git a/test/ObjectVar.py b/test/ObjectVar.py new file mode 100644 index 0000000..e5730fe --- /dev/null +++ b/test/ObjectVar.py @@ -0,0 +1,38 @@ +"""Module for testing object variables.""" + +import sys + +class TestObjectVar(BaseTestCase): + + def __TestData(self, expectedIntValue, expectedObjectValue, + expectedArrayValue): + intValue, objectValue, arrayValue = self.cursor.fetchone() + if objectValue is not None: + attributeValues = [] + for attribute in objectValue.type.attributes: + value = getattr(objectValue, attribute.name) + attributeValues.append(value) + objectValue = tuple(attributeValues) + self.failUnlessEqual(intValue, expectedIntValue) + self.failUnlessEqual(objectValue, expectedObjectValue) + self.failUnlessEqual(arrayValue, expectedArrayValue) + + def testFetchData(self): + "test fetching objects" + self.cursor.execute(""" + select + IntCol, + ObjectCol, + ArrayCol + from TestObjects + order by IntCol""") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('OBJECTCOL', cx_Oracle.OBJECT, -1, 2000, 0, 0, 1), + ('ARRAYCOL', cx_Oracle.OBJECT, -1, 2000, 0, 0, 1) ]) + self.__TestData(1, (1, 'First row', + cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0)), [5, 10, None, 20]) + self.__TestData(2, None, [3, None, 9, 12, 15]) + self.__TestData(3, (3, 'Third row', + cx_Oracle.Timestamp(2007, 6, 21, 0, 0, 0)), None) + diff --git a/test/SessionPool.py b/test/SessionPool.py new file mode 100644 index 0000000..2003fd2 --- /dev/null +++ b/test/SessionPool.py @@ -0,0 +1,80 @@ +"""Module for testing session pools.""" + +import threading + +class TestConnection(TestCase): + + def __ConnectAndDrop(self): + """Connect to the database, perform a query and drop the connection.""" + connection = self.pool.acquire() + cursor = connection.cursor() + cursor.execute("select count(*) from TestNumbers") + count, = cursor.fetchone() + self.failUnlessEqual(count, 10) + + def testPool(self): + """test that the pool is created and has the right attributes""" + pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 2, 8, 3) + self.failUnlessEqual(pool.username, USERNAME, "user name differs") + self.failUnlessEqual(pool.password, PASSWORD, "password differs") + self.failUnlessEqual(pool.tnsentry, TNSENTRY, "tnsentry differs") + self.failUnlessEqual(pool.max, 8, "max differs") + self.failUnlessEqual(pool.min, 2, "min differs") + self.failUnlessEqual(pool.increment, 3, "increment differs") + self.failUnlessEqual(pool.opened, 2, "opened differs") + self.failUnlessEqual(pool.busy, 0, "busy not 0 at start") + connection_1 = pool.acquire() + self.failUnlessEqual(pool.busy, 1, "busy not 1 after acquire") + self.failUnlessEqual(pool.opened, 2, "opened not unchanged (1)") + connection_2 = pool.acquire() + self.failUnlessEqual(pool.busy, 2, "busy not 2 after acquire") + self.failUnlessEqual(pool.opened, 2, "opened not unchanged (2)") + connection_3 = pool.acquire() + self.failUnlessEqual(pool.busy, 3, "busy not 3 after acquire") + self.failUnlessEqual(pool.opened, 5, "opened not changed (3)") + pool.release(connection_3) + self.failUnlessEqual(pool.busy, 2, "busy not 2 after release") + del connection_2 + self.failUnlessEqual(pool.busy, 1, "busy not 1 after del") + + def testRollbackOnDel(self): + "connection rolls back before being destroyed" + pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 1, 8, 3) + connection = pool.acquire() + cursor = connection.cursor() + cursor.execute("truncate table TestExecuteMany") + cursor.execute("insert into TestExecuteMany values (1)") + pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 1, 8, 3) + connection = pool.acquire() + cursor = connection.cursor() + cursor.execute("select count(*) from TestExecuteMany") + count, = cursor.fetchone() + self.failUnlessEqual(count, 0) + + def testRollbackOnRelease(self): + "connection rolls back before released back to the pool" + pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 1, 8, 3) + connection = pool.acquire() + cursor = connection.cursor() + cursor.execute("truncate table TestExecuteMany") + cursor.execute("insert into TestExecuteMany values (1)") + pool.release(connection) + pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 1, 8, 3) + connection = pool.acquire() + cursor = connection.cursor() + cursor.execute("select count(*) from TestExecuteMany") + count, = cursor.fetchone() + self.failUnlessEqual(count, 0) + + def testThreading(self): + """test session pool to database with multiple threads""" + self.pool = cx_Oracle.SessionPool(USERNAME, PASSWORD, TNSENTRY, 5, 20, + 2, threaded = True) + threads = [] + for i in range(20): + thread = threading.Thread(None, self.__ConnectAndDrop) + threads.append(thread) + thread.start() + for thread in threads: + thread.join() + diff --git a/test/SetupTest.sql b/test/SetupTest.sql new file mode 100644 index 0000000..53807b3 --- /dev/null +++ b/test/SetupTest.sql @@ -0,0 +1,411 @@ +/*----------------------------------------------------------------------------- + * SetupTest.sql + * Creates a user named "cx_Oracle" and populates its schema with the tables + * and packages necessary for performing the test suite. + *---------------------------------------------------------------------------*/ + +alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'; +alter session set nls_numeric_characters='.,'; + +create user cx_Oracle identified by dev +quota unlimited on users +default tablespace users; + +grant + create session, + create table, + create procedure, + create type +to cx_Oracle; + +-- create types +create type cx_Oracle.udt_Object as object ( + NumberValue number, + StringValue varchar2(60), + DateValue date +); +/ + +create type cx_Oracle.udt_Array as varray(10) of number; +/ + +-- create tables +create table cx_Oracle.TestNumbers ( + IntCol number(9) not null, + NumberCol number(9, 2) not null, + FloatCol float not null, + UnconstrainedCol number not null, + NullableCol number(38) +) tablespace users; + +create table cx_Oracle.TestStrings ( + IntCol number(9) not null, + StringCol varchar2(20) not null, + RawCol raw(30) not null, + FixedCharCol char(40) not null, + NullableCol varchar2(50) +) tablespace users; + +create table cx_Oracle.TestDates ( + IntCol number(9) not null, + DateCol date not null, + NullableCol date +) tablespace users; + +create table cx_Oracle.TestCLOBs ( + IntCol number(9) not null, + CLOBCol clob not null +) tablespace users; + +create table cx_Oracle.TestBLOBs ( + IntCol number(9) not null, + BLOBCol blob not null +) tablespace users; + +create table cx_Oracle.TestLongs ( + IntCol number(9) not null, + LongCol long not null +) tablespace users; + +create table cx_Oracle.TestLongRaws ( + IntCol number(9) not null, + LongRawCol long raw not null +) tablespace users; + +create table cx_Oracle.TestExecuteMany ( + IntCol number(9) not null +) tablespace users; + +create table cx_Oracle.TestObjects ( + IntCol number(9) not null, + ObjectCol cx_Oracle.udt_Object, + ArrayCol cx_Oracle.udt_Array +); + +alter table cx_Oracle.testexecutemany +add constraint testexecutemany_pk +primary key ( + intcol +) using index tablespace users; + +-- populate tables +begin + for i in 1..10 loop + insert into cx_Oracle.TestNumbers + values (i, i + i * 0.25, i + i * .75, i * i * i + i *.5, + decode(mod(i, 2), 0, null, power(143, i))); + end loop; +end; +/ + +declare + + t_RawValue raw(30); + + function ConvertHexDigit(a_Value number) return varchar2 is + begin + if a_Value between 0 and 9 then + return to_char(a_Value); + end if; + return chr(ascii('A') + a_Value - 10); + end; + + function ConvertToHex(a_Value varchar2) return varchar2 is + t_HexValue varchar2(60); + t_Digit number; + begin + for i in 1..length(a_Value) loop + t_Digit := ascii(substr(a_Value, i, 1)); + t_HexValue := t_HexValue || + ConvertHexDigit(trunc(t_Digit / 16)) || + ConvertHexDigit(mod(t_Digit, 16)); + end loop; + return t_HexValue; + end; + +begin + for i in 1..10 loop + t_RawValue := hextoraw(ConvertToHex('Raw ' || to_char(i))); + insert into cx_Oracle.TestStrings + values (i, 'String ' || to_char(i), t_RawValue, + 'Fixed Char ' || to_char(i), + decode(mod(i, 2), 0, null, 'Nullable ' || to_char(i))); + end loop; +end; +/ + +begin + for i in 1..10 loop + insert into cx_Oracle.TestDates + values (i, to_date(20021209, 'YYYYMMDD') + i + i * .1, + decode(mod(i, 2), 0, null, + to_date(20021209, 'YYYYMMDD') + i + i + i * .15)); + end loop; +end; +/ + +insert into cx_Oracle.TestObjects values (1, + cx_Oracle.udt_Object(1, 'First row', + to_date(20070306, 'YYYYMMDD')), + cx_Oracle.udt_Array(5, 10, null, 20)); + +insert into cx_Oracle.TestObjects values (2, null, + cx_Oracle.udt_Array(3, null, 9, 12, 15)); + +insert into cx_Oracle.TestObjects values (3, + cx_Oracle.udt_Object(3, 'Third row', + to_date(20070621, 'YYYYMMDD')), null); + +commit; + +-- create procedures for testing callproc() +create procedure cx_Oracle.proc_Test ( + a_InValue varchar2, + a_InOutValue in out number, + a_OutValue out number +) as +begin + a_InOutValue := a_InOutValue * length(a_InValue); + a_OutValue := length(a_InValue); +end; +/ + +create procedure cx_Oracle.proc_TestNoArgs as +begin + null; +end; +/ + +-- create functions for testing callfunc() +create function cx_Oracle.func_Test ( + a_String varchar2, + a_ExtraAmount number +) return number as +begin + return length(a_String) + a_ExtraAmount; +end; +/ + +create function cx_Oracle.func_TestNoArgs +return number as +begin + return 712; +end; +/ + +-- create packages +create or replace package cx_Oracle.pkg_TestStringArrays as + + type udt_StringList is table of varchar2(100) index by binary_integer; + + function TestInArrays ( + a_StartingLength number, + a_Array udt_StringList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_StringList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_StringList + ); + +end; +/ + +create or replace package body cx_Oracle.pkg_TestStringArrays as + + function TestInArrays ( + a_StartingLength number, + a_Array udt_StringList + ) return number is + t_Length number; + begin + t_Length := a_StartingLength; + for i in 1..a_Array.count loop + t_Length := t_Length + length(a_Array(i)); + end loop; + return t_Length; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_StringList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := 'Converted element # ' || + to_char(i) || ' originally had length ' || + to_char(length(a_Array(i))); + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_StringList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := 'Test out element # ' || to_char(i); + end loop; + end; + +end; +/ + +create or replace package cx_Oracle.pkg_TestNumberArrays as + + type udt_NumberList is table of number index by binary_integer; + + function TestInArrays ( + a_StartingValue number, + a_Array udt_NumberList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_NumberList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_NumberList + ); + +end; +/ + +create or replace package body cx_Oracle.pkg_TestNumberArrays as + + function TestInArrays ( + a_StartingValue number, + a_Array udt_NumberList + ) return number is + t_Value number; + begin + t_Value := a_StartingValue; + for i in 1..a_Array.count loop + t_Value := t_Value + a_Array(i); + end loop; + return t_Value; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_NumberList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := a_Array(i) * 10; + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_NumberList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := i * 100; + end loop; + end; + +end; +/ + +create or replace package cx_Oracle.pkg_TestDateArrays as + + type udt_DateList is table of date index by binary_integer; + + function TestInArrays ( + a_StartingValue number, + a_BaseDate date, + a_Array udt_DateList + ) return number; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out nocopy udt_DateList + ); + + procedure TestOutArrays ( + a_NumElems number, + a_Array out nocopy udt_DateList + ); + +end; +/ + +create or replace package body cx_Oracle.pkg_TestDateArrays as + + function TestInArrays ( + a_StartingValue number, + a_BaseDate date, + a_Array udt_DateList + ) return number is + t_Value number; + begin + t_Value := a_StartingValue; + for i in 1..a_Array.count loop + t_Value := t_Value + a_Array(i) - a_BaseDate; + end loop; + return t_Value; + end; + + procedure TestInOutArrays ( + a_NumElems number, + a_Array in out udt_DateList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := a_Array(i) + 7; + end loop; + end; + + procedure TestOutArrays ( + a_NumElems number, + a_Array out udt_DateList + ) is + begin + for i in 1..a_NumElems loop + a_Array(i) := to_date(20021212, 'YYYYMMDD') + i * 1.2; + end loop; + end; + +end; +/ + +create or replace package cx_Oracle.pkg_TestOutCursors as + + type udt_RefCursor is ref cursor; + + procedure TestOutCursor ( + a_MaxIntValue number, + a_Cursor out udt_RefCursor + ); + +end; +/ + +create or replace package body cx_Oracle.pkg_TestOutCursors as + + procedure TestOutCursor ( + a_MaxIntValue number, + a_Cursor out udt_RefCursor + ) is + begin + open a_Cursor for + select + IntCol, + StringCol + from TestStrings + where IntCol <= a_MaxIntValue + order by IntCol; + end; + +end; +/ + diff --git a/test/SetupTest_9i.sql b/test/SetupTest_9i.sql new file mode 100644 index 0000000..4d65e40 --- /dev/null +++ b/test/SetupTest_9i.sql @@ -0,0 +1,30 @@ +/*----------------------------------------------------------------------------- + * SetupTest_9i.sql + * Additional setup for Oracle 9i databases only. + *---------------------------------------------------------------------------*/ + +alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'; +alter session set nls_numeric_characters='.,'; + +create table cx_Oracle.TestTimestamps ( + IntCol number(9) not null, + TimestampCol timestamp not null, + NullableCol timestamp +) tablespace users; + +begin + for i in 1..10 loop + insert into cx_Oracle.TestTimestamps + values (i, to_timestamp('20021209', 'YYYYMMDD') + + to_dsinterval(to_char(i) || ' 00:00:' || to_char(i * 2) || '.' || + to_char(i * 50)), + decode(mod(i, 2), 0, to_timestamp(null, 'YYYYMMDD'), + to_timestamp('20021209', 'YYYYMMDD') + + to_dsinterval(to_char(i + 1) || ' 00:00:' || + to_char(i * 3) || '.' || to_char(i * 125)))); + end loop; +end; +/ + +commit; + diff --git a/test/StringVar.py b/test/StringVar.py new file mode 100644 index 0000000..315b945 --- /dev/null +++ b/test/StringVar.py @@ -0,0 +1,290 @@ +"""Module for testing string variables.""" + +class TestStringVar(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + self.rawData = [] + self.dataByKey = {} + for i in range(1, 11): + stringCol = "String %d" % i + fixedCharCol = ("Fixed Char %d" % i).ljust(40) + rawCol = "Raw %d" % i + if i % 2: + nullableCol = "Nullable %d" % i + else: + nullableCol = None + dataTuple = (i, stringCol, rawCol, fixedCharCol, nullableCol) + self.rawData.append(dataTuple) + self.dataByKey[i] = dataTuple + + def testBindString(self): + "test binding in a string" + self.cursor.execute(""" + select * from TestStrings + where StringCol = :p_Value""", + p_Value = "String 5") + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[5]]) + + def testBindDifferentVar(self): + "test binding a different variable on second execution" + retval_1 = self.cursor.var(cx_Oracle.STRING, 30) + retval_2 = self.cursor.var(cx_Oracle.STRING, 30) + self.cursor.execute("begin :retval := 'Called'; end;", + retval = retval_1) + self.failUnlessEqual(retval_1.getvalue(), "Called") + self.cursor.execute("begin :retval := 'Called'; end;", + retval = retval_2) + self.failUnlessEqual(retval_2.getvalue(), "Called") + + def testBindStringAfterNumber(self): + "test binding in a string after setting input sizes to a number" + self.cursor.setinputsizes(p_Value = cx_Oracle.NUMBER) + self.cursor.execute(""" + select * from TestStrings + where StringCol = :p_Value""", + p_Value = "String 6") + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[6]]) + + def testBindStringArrayDirect(self): + "test binding in a string array" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = [r[1] for r in self.rawData] + statement = """ + begin + :p_ReturnValue := pkg_TestStringArrays.TestInArrays( + :p_IntegerValue, :p_Array); + end;""" + self.cursor.execute(statement, + p_ReturnValue = returnValue, + p_IntegerValue = 5, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 86) + array = [ "String - %d" % i for i in range(15) ] + self.cursor.execute(statement, + p_IntegerValue = 8, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 163) + + def testBindStringArrayBySizes(self): + "test binding in a string array (with setinputsizes)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + self.cursor.setinputsizes(p_Array = [cx_Oracle.STRING, 10]) + array = [r[1] for r in self.rawData] + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestStringArrays.TestInArrays( + :p_IntegerValue, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_IntegerValue = 6, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 87) + + def testBindStringArrayByVar(self): + "test binding in a string array (with arrayvar)" + returnValue = self.cursor.var(cx_Oracle.NUMBER) + array = self.cursor.arrayvar(cx_Oracle.STRING, 10, 20) + array.setvalue(0, [r[1] for r in self.rawData]) + self.cursor.execute(""" + begin + :p_ReturnValue := pkg_TestStringArrays.TestInArrays( + :p_IntegerValue, :p_Array); + end;""", + p_ReturnValue = returnValue, + p_IntegerValue = 7, + p_Array = array) + self.failUnlessEqual(returnValue.getvalue(), 88) + + def testBindInOutStringArrayByVar(self): + "test binding in/out a string array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.STRING, 10, 100) + originalData = [r[1] for r in self.rawData] + expectedData = ["Converted element # %d originally had length %d" % \ + (i, len(originalData[i - 1])) for i in range(1, 6)] + \ + originalData[5:] + array.setvalue(0, originalData) + self.cursor.execute(""" + begin + pkg_TestStringArrays.TestInOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 5, + p_Array = array) + self.failUnlessEqual(array.getvalue(), expectedData) + + def testBindOutStringArrayByVar(self): + "test binding out a string array (with arrayvar)" + array = self.cursor.arrayvar(cx_Oracle.STRING, 6, 100) + expectedData = ["Test out element # %d" % i for i in range(1, 7)] + self.cursor.execute(""" + begin + pkg_TestStringArrays.TestOutArrays(:p_NumElems, :p_Array); + end;""", + p_NumElems = 6, + p_Array = array) + self.failUnlessEqual(array.getvalue(), expectedData) + + def testBindRaw(self): + "test binding in a raw" + self.cursor.setinputsizes(p_Value = cx_Oracle.BINARY) + self.cursor.execute(""" + select * from TestStrings + where RawCol = :p_Value""", + p_Value = "Raw 4") + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[4]]) + + def testBindAndFetchRowid(self): + "test binding (and fetching) a rowid" + self.cursor.execute(""" + select rowid + from TestStrings + where IntCol = 3""") + rowid, = self.cursor.fetchone() + self.cursor.execute(""" + select * + from TestStrings + where rowid = :p_Value""", + p_Value = rowid) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[3]]) + + def testBindNull(self): + "test binding in a null" + self.cursor.execute(""" + select * from TestStrings + where StringCol = :p_Value""", + p_Value = None) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testBindOutSetInputSizesByType(self): + "test binding out with set input sizes defined (by type)" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.STRING) + self.cursor.execute(""" + begin + :p_Value := 'TSI'; + end;""") + self.failUnlessEqual(vars["p_Value"].getvalue(), "TSI") + + def testBindOutSetInputSizesByInteger(self): + "test binding out with set input sizes defined (by integer)" + vars = self.cursor.setinputsizes(p_Value = 30) + self.cursor.execute(""" + begin + :p_Value := 'TSI (I)'; + end;""") + self.failUnlessEqual(vars["p_Value"].getvalue(), "TSI (I)") + + def testBindInOutSetInputSizesByType(self): + "test binding in/out with set input sizes defined (by type)" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.STRING) + self.cursor.execute(""" + begin + :p_Value := :p_Value || ' TSI'; + end;""", + p_Value = "InVal") + self.failUnlessEqual(vars["p_Value"].getvalue(), "InVal TSI") + + def testBindInOutSetInputSizesByInteger(self): + "test binding in/out with set input sizes defined (by integer)" + vars = self.cursor.setinputsizes(p_Value = 30) + self.cursor.execute(""" + begin + :p_Value := :p_Value || ' TSI (I)'; + end;""", + p_Value = "InVal") + self.failUnlessEqual(vars["p_Value"].getvalue(), "InVal TSI (I)") + + def testBindOutVar(self): + "test binding out with cursor.var() method" + var = self.cursor.var(cx_Oracle.STRING) + self.cursor.execute(""" + begin + :p_Value := 'TSI (VAR)'; + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), "TSI (VAR)") + + def testBindInOutVarDirectSet(self): + "test binding in/out with cursor.var() method" + var = self.cursor.var(cx_Oracle.STRING) + var.setvalue(0, "InVal") + self.cursor.execute(""" + begin + :p_Value := :p_Value || ' TSI (VAR)'; + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), "InVal TSI (VAR)") + + def testBindLongString(self): + "test that binding a long string succeeds" + self.cursor.execute(""" + declare + t_Temp varchar2(10000); + begin + t_Temp := :bigString; + end;""", + bigString = "X" * 10000) + + def testBindLongStringAfterSettingSize(self): + "test that setinputsizes() returns a long variable" + var = self.cursor.setinputsizes(test = 90000)["test"] + self.failUnlessEqual(type(var), cx_Oracle.LONG_STRING) + inString = "1234567890" * 9000 + var.setvalue(0, inString) + outString = var.getvalue() + self.failUnlessEqual(inString, outString, + "output does not match: in was %d, out was %d" % \ + (len(inString), len(outString))) + + def testStringMaximumReached(self): + "test that an error is raised when maximum string length exceeded" + var = self.cursor.setinputsizes(test = 100)["test"] + inString = "1234567890" * 400 + var.setvalue(0, inString) + outString = var.getvalue() + self.failUnlessEqual(inString, outString, + "output does not match: in was %d, out was %d" % \ + (len(inString), len(outString))) + badStringSize = 4000 * self.connection.maxBytesPerCharacter + 1 + inString = "X" * badStringSize + self.failUnlessRaises(ValueError, var.setvalue, 0, inString) + + def testCursorDescription(self): + "test cursor description is accurate" + self.cursor.execute("select * from TestStrings") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('STRINGCOL', cx_Oracle.STRING, 20, 20, 0, 0, 0), + ('RAWCOL', cx_Oracle.BINARY, 30, 30, 0, 0, 0), + ('FIXEDCHARCOL', cx_Oracle.FIXED_CHAR, 40, 40, 0, 0, 0), + ('NULLABLECOL', cx_Oracle.STRING, 50, 50, 0, 0, 1) ]) + + def testFetchAll(self): + "test that fetching all of the data returns the correct results" + self.cursor.execute("select * From TestStrings order by IntCol") + self.failUnlessEqual(self.cursor.fetchall(), self.rawData) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testFetchMany(self): + "test that fetching data in chunks returns the correct results" + self.cursor.execute("select * From TestStrings order by IntCol") + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[0:3]) + self.failUnlessEqual(self.cursor.fetchmany(2), self.rawData[3:5]) + self.failUnlessEqual(self.cursor.fetchmany(4), self.rawData[5:9]) + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[9:]) + self.failUnlessEqual(self.cursor.fetchmany(3), []) + + def testFetchOne(self): + "test that fetching a single row returns the correct results" + self.cursor.execute(""" + select * + from TestStrings + where IntCol in (3, 4) + order by IntCol""") + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[3]) + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[4]) + self.failUnlessEqual(self.cursor.fetchone(), None) + +if __name__ == "__main__": + print "Testing cx_Oracle version", cx_Oracle.version + unittest.main() + diff --git a/test/TestEnv.py b/test/TestEnv.py new file mode 100644 index 0000000..db082bf --- /dev/null +++ b/test/TestEnv.py @@ -0,0 +1,17 @@ +"""Define test environment.""" + +import cx_Oracle +import os +import unittest + +def GetValue(name, label): + value = os.environ.get("CX_ORACLE_" + name) + if value is None: + value = raw_input(label + ": ") + return value + +USERNAME = GetValue("USERNAME", "user name") +PASSWORD = GetValue("PASSWORD", "password") +TNSENTRY = GetValue("TNSENTRY", "TNS entry") +ARRAY_SIZE = int(GetValue("ARRAY_SIZE", "array size")) + diff --git a/test/TimestampVar.py b/test/TimestampVar.py new file mode 100644 index 0000000..406e574 --- /dev/null +++ b/test/TimestampVar.py @@ -0,0 +1,130 @@ +"""Module for testing timestamp variables.""" + +import time + +class TestTimestampVar(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + self.rawData = [] + self.dataByKey = {} + for i in range(1, 11): + timeTuple = (2002, 12, 9, 0, 0, 0, 0, 0, -1) + timeInTicks = time.mktime(timeTuple) + i * 86400 + dateValue = cx_Oracle.TimestampFromTicks(int(timeInTicks)) + strValue = str(i * 50) + fsecond = int(strValue + "0" * (6 - len(strValue))) + dateCol = cx_Oracle.Timestamp(dateValue.year, dateValue.month, + dateValue.day, dateValue.hour, dateValue.minute, + i * 2, fsecond) + if i % 2: + timeInTicks = time.mktime(timeTuple) + i * 86400 + 86400 + dateValue = cx_Oracle.TimestampFromTicks(int(timeInTicks)) + strValue = str(i * 125) + fsecond = int(strValue + "0" * (6 - len(strValue))) + nullableCol = cx_Oracle.Timestamp(dateValue.year, + dateValue.month, dateValue.day, dateValue.hour, + dateValue.minute, i * 3, fsecond) + else: + nullableCol = None + tuple = (i, dateCol, nullableCol) + self.rawData.append(tuple) + self.dataByKey[i] = tuple + + def testBindTimestamp(self): + "test binding in a timestamp" + self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP) + self.cursor.execute(""" + select * from TestTimestamps + where TimestampCol = :value""", + value = cx_Oracle.Timestamp(2002, 12, 14, 0, 0, 10, 250000)) + self.failUnlessEqual(self.cursor.fetchall(), [self.dataByKey[5]]) + + def testBindNull(self): + "test binding in a null" + self.cursor.setinputsizes(p_Value = cx_Oracle.TIMESTAMP) + self.cursor.execute(""" + select * from TestTimestamps + where TimestampCol = :p_Value""", + p_Value = None) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testBindOutSetInputSizes(self): + "test binding out with set input sizes defined" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.TIMESTAMP) + self.cursor.execute(""" + begin + :p_Value := to_timestamp('20021209', 'YYYYMMDD'); + end;""") + self.failUnlessEqual(vars["p_Value"].getvalue(), + cx_Oracle.Timestamp(2002, 12, 9)) + + def testBindInOutSetInputSizes(self): + "test binding in/out with set input sizes defined" + vars = self.cursor.setinputsizes(p_Value = cx_Oracle.TIMESTAMP) + self.cursor.execute(""" + begin + :p_Value := :p_Value + 5.25; + end;""", + p_Value = cx_Oracle.Timestamp(2002, 12, 12, 10, 0, 0)) + self.failUnlessEqual(vars["p_Value"].getvalue(), + cx_Oracle.Timestamp(2002, 12, 17, 16, 0, 0)) + + def testBindOutVar(self): + "test binding out with cursor.var() method" + var = self.cursor.var(cx_Oracle.TIMESTAMP) + self.cursor.execute(""" + begin + :p_Value := to_date('20021231 12:31:00', + 'YYYYMMDD HH24:MI:SS'); + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), + cx_Oracle.Timestamp(2002, 12, 31, 12, 31, 0)) + + def testBindInOutVarDirectSet(self): + "test binding in/out with cursor.var() method" + var = self.cursor.var(cx_Oracle.TIMESTAMP) + var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0)) + self.cursor.execute(""" + begin + :p_Value := :p_Value + 5.25; + end;""", + p_Value = var) + self.failUnlessEqual(var.getvalue(), + cx_Oracle.Timestamp(2002, 12, 14, 12, 0, 0)) + + def testCursorDescription(self): + "test cursor description is accurate" + self.cursor.execute("select * from TestTimestamps") + self.failUnlessEqual(self.cursor.description, + [ ('INTCOL', cx_Oracle.NUMBER, 10, 22, 9, 0, 0), + ('TIMESTAMPCOL', cx_Oracle.TIMESTAMP, -1, 11, 0, 0, 0), + ('NULLABLECOL', cx_Oracle.TIMESTAMP, -1, 11, 0, 0, 1) ]) + + def testFetchAll(self): + "test that fetching all of the data returns the correct results" + self.cursor.execute("select * From TestTimestamps order by IntCol") + self.failUnlessEqual(self.cursor.fetchall(), self.rawData) + self.failUnlessEqual(self.cursor.fetchall(), []) + + def testFetchMany(self): + "test that fetching data in chunks returns the correct results" + self.cursor.execute("select * From TestTimestamps order by IntCol") + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[0:3]) + self.failUnlessEqual(self.cursor.fetchmany(2), self.rawData[3:5]) + self.failUnlessEqual(self.cursor.fetchmany(4), self.rawData[5:9]) + self.failUnlessEqual(self.cursor.fetchmany(3), self.rawData[9:]) + self.failUnlessEqual(self.cursor.fetchmany(3), []) + + def testFetchOne(self): + "test that fetching a single row returns the correct results" + self.cursor.execute(""" + select * + from TestTimestamps + where IntCol in (3, 4) + order by IntCol""") + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[3]) + self.failUnlessEqual(self.cursor.fetchone(), self.dataByKey[4]) + self.failUnlessEqual(self.cursor.fetchone(), None) + diff --git a/test/test.py b/test/test.py new file mode 100644 index 0000000..72fb14b --- /dev/null +++ b/test/test.py @@ -0,0 +1,67 @@ +"""Runs all defined unit tests.""" + +import cx_Oracle +import imp +import os +import sys +import unittest + +print "Running tests for cx_Oracle version", cx_Oracle.version + +import TestEnv + +moduleNames = [ + "Connection", + "Cursor", + "CursorVar", + "DateTimeVar", + "LobVar", + "LongVar", + "NumberVar", + "ObjectVar", + "StringVar" +] + +if hasattr(cx_Oracle, "TIMESTAMP"): + moduleNames.append("TimestampVar") +if hasattr(cx_Oracle, "SessionPool"): + moduleNames.append("SessionPool") + +class BaseTestCase(unittest.TestCase): + + def setUp(self): + self.connection = cx_Oracle.connect(TestEnv.USERNAME, + TestEnv.PASSWORD, TestEnv.TNSENTRY) + self.cursor = self.connection.cursor() + self.cursor.arraysize = TestEnv.ARRAY_SIZE + + def tearDown(self): + del self.cursor + del self.connection + + +loader = unittest.TestLoader() +runner = unittest.TextTestRunner(verbosity = 2) +failures = [] +for name in moduleNames: + fileName = name + ".py" + print + print "Running tests in", fileName + module = imp.new_module(name) + setattr(module, "USERNAME", TestEnv.USERNAME) + setattr(module, "PASSWORD", TestEnv.PASSWORD) + setattr(module, "TNSENTRY", TestEnv.TNSENTRY) + setattr(module, "ARRAY_SIZE", TestEnv.ARRAY_SIZE) + setattr(module, "TestCase", unittest.TestCase) + setattr(module, "BaseTestCase", BaseTestCase) + setattr(module, "cx_Oracle", cx_Oracle) + execfile(fileName, module.__dict__) + tests = loader.loadTestsFromModule(module) + result = runner.run(tests) + if not result.wasSuccessful(): + failures.append(name) +if failures: + print "***** Some tests in the following modules failed. *****" + for name in failures: + print " %s" % name + diff --git a/test/test_dbapi20.py b/test/test_dbapi20.py new file mode 100644 index 0000000..13fd91c --- /dev/null +++ b/test/test_dbapi20.py @@ -0,0 +1,39 @@ +"""Driver specific portion of the DB API test suite provided by Stuart Bishop + available at http://stuartbishop.net/Software/DBAPI20TestSuite/""" + +import cx_Oracle +import dbapi20 +import unittest + +import TestEnv + +class TestSuite(dbapi20.DatabaseAPI20Test): + + connect_args = (TestEnv.USERNAME, TestEnv.PASSWORD, TestEnv.TNSENTRY) + driver = cx_Oracle + + # not implemented; use a string instead + def test_Binary(self): + pass + + # not implemented; see cx_Oracle specific test suite instead + def test_callproc(self): + pass + + # not implemented; Oracle does not support the concept + def test_nextset(self): + pass + + # not implemented; see cx_Oracle specific test suite instead + def test_setinputsizes(self): + pass + + # not implemented; see cx_Oracle specific test suite instead + def test_setoutputsize(self): + pass + + +if __name__ == "__main__": + print "Testing cx_Oracle version", cx_Oracle.version + unittest.main() +