python-cx_Oracle/Connection.c
2016-03-01 15:34:05 -07:00

1997 lines
74 KiB
C

//-----------------------------------------------------------------------------
// 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;
udt_SessionPool *sessionPool;
PyObject *inputTypeHandler;
PyObject *outputTypeHandler;
PyObject *username;
PyObject *dsn;
PyObject *version;
ub4 commitMode;
int autocommit;
int release;
int attached;
} udt_Connection;
//-----------------------------------------------------------------------------
// constants for the OCI attributes
//-----------------------------------------------------------------------------
static ub4 gc_ClientIdentifierAttribute = OCI_ATTR_CLIENT_IDENTIFIER;
static ub4 gc_ModuleAttribute = OCI_ATTR_MODULE;
static ub4 gc_ActionAttribute = OCI_ATTR_ACTION;
static ub4 gc_ClientInfoAttribute = OCI_ATTR_CLIENT_INFO;
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
static ub4 gc_CurrentSchemaAttribute = OCI_ATTR_CURRENT_SCHEMA;
#endif
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 2)
static ub4 gc_EditionAttribute = OCI_ATTR_EDITION;
#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*, 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_GetEncoding(udt_Connection*, void*);
static PyObject *Connection_GetNationalEncoding(udt_Connection*, void*);
static PyObject *Connection_GetMaxBytesPerCharacter(udt_Connection*, void*);
static PyObject *Connection_ContextManagerEnter(udt_Connection*, PyObject*);
static PyObject *Connection_ContextManagerExit(udt_Connection*, PyObject*);
static PyObject *Connection_ChangePasswordExternal(udt_Connection*, PyObject*);
static PyObject *Connection_GetType(udt_Connection*, PyObject*);
static PyObject *Connection_GetStmtCacheSize(udt_Connection*, void*);
static PyObject *Connection_NewEnqueueOptions(udt_Connection*, PyObject*);
static PyObject *Connection_NewDequeueOptions(udt_Connection*, PyObject*);
static PyObject *Connection_NewMessageProperties(udt_Connection*, PyObject*);
static PyObject *Connection_Dequeue(udt_Connection*, PyObject*, PyObject*);
static PyObject *Connection_Enqueue(udt_Connection*, PyObject*, PyObject*);
static int Connection_SetStmtCacheSize(udt_Connection*, PyObject*, void*);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
static PyObject *Connection_GetOCIAttr(udt_Connection*, ub4*);
#endif
static int Connection_SetOCIAttr(udt_Connection*, PyObject*, ub4*);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
#if !defined(AIX5) || ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 1)
static PyObject *Connection_Ping(udt_Connection*, PyObject*);
#endif
static PyObject *Connection_Shutdown(udt_Connection*, PyObject*, PyObject*);
static PyObject *Connection_Startup(udt_Connection*, PyObject*, PyObject*);
static PyObject *Connection_Subscribe(udt_Connection*, PyObject*, PyObject*);
#endif
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
static PyObject *Connection_GetLTXID(udt_Connection*, void*);
#endif
//-----------------------------------------------------------------------------
// declaration of methods for Python type "Connection"
//-----------------------------------------------------------------------------
static PyMethodDef g_ConnectionMethods[] = {
{ "cursor", (PyCFunction) Connection_NewCursor,
METH_VARARGS | METH_KEYWORDS },
{ "commit", (PyCFunction) Connection_Commit, METH_NOARGS },
{ "rollback", (PyCFunction) Connection_Rollback, METH_NOARGS },
{ "begin", (PyCFunction) Connection_Begin, METH_VARARGS },
{ "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 },
{ "__enter__", (PyCFunction) Connection_ContextManagerEnter, METH_NOARGS },
{ "__exit__", (PyCFunction) Connection_ContextManagerExit, METH_VARARGS },
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
#if !defined(AIX5) || ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 1)
{ "ping", (PyCFunction) Connection_Ping, METH_NOARGS },
#endif
{ "shutdown", (PyCFunction) Connection_Shutdown,
METH_VARARGS | METH_KEYWORDS},
{ "startup", (PyCFunction) Connection_Startup,
METH_VARARGS | METH_KEYWORDS},
{ "subscribe", (PyCFunction) Connection_Subscribe,
METH_VARARGS | METH_KEYWORDS},
#endif
{ "changepassword", (PyCFunction) Connection_ChangePasswordExternal,
METH_VARARGS },
{ "gettype", (PyCFunction) Connection_GetType, METH_VARARGS },
{ "deqoptions", (PyCFunction) Connection_NewDequeueOptions, METH_NOARGS },
{ "enqoptions", (PyCFunction) Connection_NewEnqueueOptions, METH_NOARGS },
{ "msgproperties", (PyCFunction) Connection_NewMessageProperties,
METH_NOARGS },
{ "deq", (PyCFunction) Connection_Dequeue, METH_VARARGS | METH_KEYWORDS },
{ "enq", (PyCFunction) Connection_Enqueue, METH_VARARGS | METH_KEYWORDS },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of members for Python type "Connection"
//-----------------------------------------------------------------------------
static PyMemberDef g_ConnectionMembers[] = {
{ "username", T_OBJECT, offsetof(udt_Connection, username), READONLY },
{ "dsn", T_OBJECT, offsetof(udt_Connection, dsn), READONLY },
{ "tnsentry", T_OBJECT, offsetof(udt_Connection, dsn), READONLY },
{ "autocommit", T_INT, offsetof(udt_Connection, autocommit), 0 },
{ "inputtypehandler", T_OBJECT,
offsetof(udt_Connection, inputTypeHandler), 0 },
{ "outputtypehandler", T_OBJECT,
offsetof(udt_Connection, outputTypeHandler), 0 },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of calculated members for Python type "Connection"
//-----------------------------------------------------------------------------
static PyGetSetDef g_ConnectionCalcMembers[] = {
{ "version", (getter) Connection_GetVersion, 0, 0, 0 },
{ "encoding", (getter) Connection_GetEncoding, 0, 0, 0 },
{ "nencoding", (getter) Connection_GetNationalEncoding, 0, 0, 0 },
{ "maxBytesPerCharacter", (getter) Connection_GetMaxBytesPerCharacter,
0, 0, 0 },
{ "stmtcachesize", (getter) Connection_GetStmtCacheSize,
(setter) Connection_SetStmtCacheSize, 0, 0 },
{ "module", 0, (setter) Connection_SetOCIAttr, 0, &gc_ModuleAttribute },
{ "action", 0, (setter) Connection_SetOCIAttr, 0, &gc_ActionAttribute },
{ "clientinfo", 0, (setter) Connection_SetOCIAttr, 0,
&gc_ClientInfoAttribute },
{ "client_identifier", 0, (setter) Connection_SetOCIAttr, 0,
&gc_ClientIdentifierAttribute },
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
{ "current_schema", (getter) Connection_GetOCIAttr,
(setter) Connection_SetOCIAttr, 0, &gc_CurrentSchemaAttribute },
#endif
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 2)
{ "edition", (getter) Connection_GetOCIAttr, 0, 0, &gc_EditionAttribute },
#endif
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
{ "ltxid", (getter) Connection_GetLTXID, 0, 0, 0 },
#endif
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of Python type "Connection"
//-----------------------------------------------------------------------------
static PyTypeObject g_ConnectionType = {
PyVarObject_HEAD_INIT(NULL, 0)
"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;
}
//-----------------------------------------------------------------------------
// Connection_GetConnection()
// Get a connection using the OCISessionGet() interface rather than using
// the low level interface for connecting.
//-----------------------------------------------------------------------------
static int Connection_GetConnection(
udt_Connection *self, // connection
udt_SessionPool *pool, // pool to acquire connection from
PyObject *passwordObj, // password
PyObject *cclassObj, // connection class (DRCP)
ub4 purity) // purity (DRCP)
{
udt_Environment *environment;
int externalAuth, proxyAuth;
udt_Buffer buffer;
OCIAuthInfo *authInfo;
PyObject *dbNameObj;
boolean found;
sword status;
ub4 mode;
// set things up for the call to acquire a session
authInfo = NULL;
externalAuth = proxyAuth = 0;
if (pool) {
environment = pool->environment;
dbNameObj = pool->name;
mode = OCI_SESSGET_SPOOL;
externalAuth = pool->externalAuth;
if (!pool->homogeneous && pool->username && self->username) {
proxyAuth = PyObject_RichCompareBool(self->username,
pool->username, Py_NE);
if (proxyAuth < 0)
return -1;
mode = mode | OCI_SESSGET_CREDPROXY;
}
} else {
environment = self->environment;
dbNameObj = self->dsn;
mode = OCI_SESSGET_STMTCACHE;
}
// set up authorization handle, if needed
if (!pool || cclassObj || proxyAuth) {
// create authorization handle
status = OCIHandleAlloc(environment->handle, (dvoid*) &authInfo,
OCI_HTYPE_AUTHINFO, 0, NULL);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): allocate handle") < 0)
return -1;
// set the user name, if applicable
externalAuth = 1;
if (cxBuffer_FromObject(&buffer, self->username,
self->environment->encoding) < 0)
return -1;
if (buffer.size > 0) {
externalAuth = 0;
status = OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO,
(text*) buffer.ptr, buffer.size, OCI_ATTR_USERNAME,
environment->errorHandle);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): set user name") < 0) {
cxBuffer_Clear(&buffer);
return -1;
}
}
cxBuffer_Clear(&buffer);
// set the password, if applicable
if (cxBuffer_FromObject(&buffer, passwordObj,
self->environment->encoding) < 0)
return -1;
if (buffer.size > 0) {
externalAuth = 0;
status = OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO,
(text*) buffer.ptr, buffer.size, OCI_ATTR_PASSWORD,
environment->errorHandle);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): set password") < 0) {
cxBuffer_Clear(&buffer);
return -1;
}
}
cxBuffer_Clear(&buffer);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 1)
// set the connection class, if applicable
if (cxBuffer_FromObject(&buffer, cclassObj,
self->environment->encoding) < 0)
return -1;
if (buffer.size > 0) {
status = OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO,
(text*) buffer.ptr, buffer.size, OCI_ATTR_CONNECTION_CLASS,
environment->errorHandle);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): set connection class") < 0) {
cxBuffer_Clear(&buffer);
return -1;
}
}
cxBuffer_Clear(&buffer);
// set the purity, if applicable
if (purity != OCI_ATTR_PURITY_DEFAULT) {
status = OCIAttrSet(authInfo, OCI_HTYPE_AUTHINFO, &purity,
sizeof(purity), OCI_ATTR_PURITY,
environment->errorHandle);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): set purity") < 0)
return -1;
}
#endif
}
// external auth requested (no username/password or specified via pool)
if (externalAuth)
mode |= OCI_SESSGET_CREDEXT;
// acquire the new session
if (cxBuffer_FromObject(&buffer, dbNameObj,
self->environment->encoding) < 0)
return -1;
Py_BEGIN_ALLOW_THREADS
status = OCISessionGet(environment->handle, environment->errorHandle,
&self->handle, authInfo, (text*) buffer.ptr, buffer.size, NULL, 0,
NULL, NULL, &found, mode);
Py_END_ALLOW_THREADS
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(environment, status,
"Connection_GetConnection(): get connection") < 0)
return -1;
// eliminate the authorization handle immediately, if applicable
if (authInfo)
OCIHandleFree(authInfo, OCI_HTYPE_AUTHINFO);
// copy members in the case where a pool is being used
if (pool) {
if (!proxyAuth) {
Py_INCREF(pool->username);
self->username = pool->username;
}
Py_INCREF(pool->dsn);
self->dsn = pool->dsn;
Py_INCREF(pool);
self->sessionPool = pool;
}
self->release = 1;
return 0;
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
//-----------------------------------------------------------------------------
// Connection_GetOCIAttr()
// Get the value of the OCI attribute.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetOCIAttr(
udt_Connection *self, // connection to set
ub4 *attribute) // OCI attribute type
{
OCISession *sessionHandle;
udt_Buffer buffer;
ub4 bufferSize;
sword status;
// make sure connection is connected
if (Connection_IsConnected(self) < 0)
return NULL;
// acquire the session handle
status = OCIAttrGet(self->handle, OCI_HTYPE_SVCCTX,
(dvoid**) &sessionHandle, 0, OCI_ATTR_SESSION,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_GetOCIAttr(): determine session handle") < 0)
return NULL;
// get the value from the OCI
status = OCIAttrGet(sessionHandle, OCI_HTYPE_SESSION,
(text**) &buffer.ptr, &bufferSize, *attribute,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_GetOCIAttr()") < 0)
return NULL;
if (!buffer.ptr) {
Py_INCREF(Py_None);
return Py_None;
}
buffer.size = bufferSize;
return cxString_FromEncodedString(buffer.ptr, buffer.size,
self->environment->encoding);
}
#endif
//-----------------------------------------------------------------------------
// 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
{
OCISession *sessionHandle;
udt_Buffer buffer;
sword status;
// make sure connection is connected
if (Connection_IsConnected(self) < 0)
return -1;
// acquire the session handle
status = OCIAttrGet(self->handle, OCI_HTYPE_SVCCTX,
(dvoid**) &sessionHandle, 0, OCI_ATTR_SESSION,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_SetOCIAttr(): determine session handle") < 0)
return -1;
// set the value in the OCI
if (cxBuffer_FromObject(&buffer, value, self->environment->encoding))
return -1;
status = OCIAttrSet(sessionHandle, OCI_HTYPE_SESSION, (text*) buffer.ptr,
buffer.size, *attribute, self->environment->errorHandle);
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(self->environment, status,
"Connection_SetOCIAttr(): set value") < 0)
return -1;
return 0;
}
//-----------------------------------------------------------------------------
// 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;
self->attached = 1;
return 0;
}
//-----------------------------------------------------------------------------
// Connection_ChangePassword()
// Change the password for the given connection.
//-----------------------------------------------------------------------------
static int Connection_ChangePassword(
udt_Connection *self, // connection
PyObject *oldPasswordObj, // old password
PyObject *newPasswordObj) // new password
{
udt_Buffer usernameBuffer, oldPasswordBuffer, newPasswordBuffer;
sword status;
if (cxBuffer_FromObject(&usernameBuffer, self->username,
self->environment->encoding) < 0)
return -1;
if (cxBuffer_FromObject(&oldPasswordBuffer, oldPasswordObj,
self->environment->encoding) < 0) {
cxBuffer_Clear(&usernameBuffer);
return -1;
}
if (cxBuffer_FromObject(&newPasswordBuffer, newPasswordObj,
self->environment->encoding) < 0) {
cxBuffer_Clear(&usernameBuffer);
cxBuffer_Clear(&oldPasswordBuffer);
return -1;
}
// begin the session
Py_BEGIN_ALLOW_THREADS
status = OCIPasswordChange(self->handle, self->environment->errorHandle,
(text*) usernameBuffer.ptr, usernameBuffer.size,
(text*) oldPasswordBuffer.ptr, oldPasswordBuffer.size,
(text*) newPasswordBuffer.ptr, newPasswordBuffer.size,
OCI_AUTH);
Py_END_ALLOW_THREADS
cxBuffer_Clear(&usernameBuffer);
cxBuffer_Clear(&oldPasswordBuffer);
cxBuffer_Clear(&newPasswordBuffer);
if (Environment_CheckForError(self->environment, status,
"Connection_ChangePassword(): change password") < 0)
return -1;
return 0;
}
//-----------------------------------------------------------------------------
// Connection_ChangePasswordExternal()
// Change the password for the given connection.
//-----------------------------------------------------------------------------
static PyObject *Connection_ChangePasswordExternal(
udt_Connection *self, // connection
PyObject *args) // arguments
{
PyObject *oldPasswordObj, *newPasswordObj;
// parse the arguments
if (!PyArg_ParseTuple(args, "O!O!", cxString_Type, &oldPasswordObj,
cxString_Type, &newPasswordObj))
return NULL;
if (Connection_ChangePassword(self, oldPasswordObj, newPasswordObj) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
//-----------------------------------------------------------------------------
// Connection_Connect()
// Create a new connection object by connecting to the database.
//-----------------------------------------------------------------------------
static int Connection_Connect(
udt_Connection *self, // connection
ub4 mode, // mode to connect as
int twophase, // allow two phase commit?
PyObject *passwordObj, // password
PyObject *newPasswordObj, // new password (if desired)
PyObject *moduleObj, // session "module" value
PyObject *actionObj, // session "action" value
PyObject *clientinfoObj, // session "clientinfo" value
PyObject *editionObj) // session "edition" value
{
ub4 credentialType = OCI_CRED_EXT;
udt_Buffer buffer;
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
if (cxBuffer_FromObject(&buffer, self->dsn,
self->environment->encoding) < 0)
return -1;
Py_BEGIN_ALLOW_THREADS
status = OCIServerAttach(self->serverHandle,
self->environment->errorHandle, (text*) buffer.ptr, buffer.size,
OCI_DEFAULT);
Py_END_ALLOW_THREADS
cxBuffer_Clear(&buffer);
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 (cxBuffer_FromObject(&buffer, self->username,
self->environment->encoding) < 0)
return -1;
if (buffer.size > 0) {
credentialType = OCI_CRED_RDBMS;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_USERNAME,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set user name") < 0) {
cxBuffer_Clear(&buffer);
return -1;
}
}
cxBuffer_Clear(&buffer);
// set password in session handle
if (cxBuffer_FromObject(&buffer, passwordObj,
self->environment->encoding) < 0)
return -1;
if (buffer.size > 0) {
credentialType = OCI_CRED_RDBMS;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_PASSWORD,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set password") < 0) {
cxBuffer_Clear(&buffer);
return -1;
}
}
cxBuffer_Clear(&buffer);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11,1)
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) DRIVER_NAME, strlen(DRIVER_NAME), OCI_ATTR_DRIVER_NAME,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set driver name") < 0)
return -1;
#endif
// 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;
if (moduleObj) {
if (cxBuffer_FromObject(&buffer, moduleObj,
self->environment->encoding))
return -1;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_MODULE,
self->environment->errorHandle);
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set module") < 0)
return -1;
}
if (actionObj) {
if (cxBuffer_FromObject(&buffer, actionObj,
self->environment->encoding))
return -1;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_ACTION,
self->environment->errorHandle);
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set action") < 0)
return -1;
}
if (clientinfoObj) {
if (cxBuffer_FromObject(&buffer, clientinfoObj,
self->environment->encoding))
return -1;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_CLIENT_INFO,
self->environment->errorHandle);
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set clientinfo") < 0)
return -1;
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 2)
if (editionObj) {
if (cxBuffer_FromObject(&buffer, editionObj,
self->environment->encoding))
return -1;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
(text*) buffer.ptr, buffer.size, OCI_ATTR_EDITION,
self->environment->errorHandle);
cxBuffer_Clear(&buffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Connect(): set edition") < 0)
return -1;
}
#endif
// if a new password has been specified, change it which will also
// establish the session
if (newPasswordObj)
return Connection_ChangePassword(self, passwordObj, newPasswordObj);
// 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"
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
#include "Subscription.c"
#endif
#include "AQ.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;
return (PyObject*) self;
}
//-----------------------------------------------------------------------------
// Connection_SplitComponent()
// Split the component out of the source and replace the source with the
// characters up to the split string and put the characters after the split
// string in to the target.
//-----------------------------------------------------------------------------
static int Connection_SplitComponent(
PyObject **sourceObj, // source object to split
PyObject **targetObj, // target object (for component)
const char *splitString) // split string (assume one character)
{
PyObject *temp, *posObj;
Py_ssize_t size, pos;
if (!*sourceObj || *targetObj)
return 0;
posObj = PyObject_CallMethod(*sourceObj, "find", "s", splitString);
if (!posObj)
return -1;
pos = PyInt_AsLong(posObj);
Py_DECREF(posObj);
if (PyErr_Occurred())
return -1;
if (pos >= 0) {
size = PySequence_Size(*sourceObj);
if (PyErr_Occurred())
return -1;
*targetObj = PySequence_GetSlice(*sourceObj, pos + 1, size);
if (!*targetObj)
return -1;
temp = PySequence_GetSlice(*sourceObj, 0, pos);
if (!temp)
return -1;
*sourceObj = temp;
}
return 0;
}
//-----------------------------------------------------------------------------
// Connection_Init()
// Initialize the connection members.
//-----------------------------------------------------------------------------
static int Connection_Init(
udt_Connection *self, // connection
PyObject *args, // arguments
PyObject *keywordArgs) // keyword arguments
{
PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj;
PyObject *threadedObj, *twophaseObj, *eventsObj, *newPasswordObj;
PyObject *moduleObj, *actionObj, *clientinfoObj;
int threaded, twophase, events;
char *encoding, *nencoding;
ub4 connectMode, purity;
udt_SessionPool *pool;
OCISvcCtx *handle;
// define keyword arguments
static char *keywordList[] = { "user", "password", "dsn", "mode",
"handle", "pool", "threaded", "twophase", "events", "cclass",
"purity", "newpassword", "encoding", "nencoding", "module",
"action", "clientinfo", "edition", NULL };
// parse arguments
pool = NULL;
handle = NULL;
connectMode = OCI_DEFAULT;
threadedObj = twophaseObj = eventsObj = newPasswordObj = NULL;
usernameObj = passwordObj = dsnObj = cclassObj = editionObj = NULL;
moduleObj = actionObj = clientinfoObj = NULL;
threaded = twophase = events = purity = 0;
encoding = nencoding = NULL;
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 1)
purity = OCI_ATTR_PURITY_DEFAULT;
#endif
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
"|OOOiiO!OOOOiOssOOOO", keywordList, &usernameObj, &passwordObj,
&dsnObj, &connectMode, &handle, &g_SessionPoolType, &pool,
&threadedObj, &twophaseObj, &eventsObj, &cclassObj, &purity,
&newPasswordObj, &encoding, &nencoding, &moduleObj, &actionObj,
&clientinfoObj, &editionObj))
return -1;
if (threadedObj) {
threaded = PyObject_IsTrue(threadedObj);
if (threaded < 0)
return -1;
}
if (twophaseObj) {
twophase = PyObject_IsTrue(twophaseObj);
if (twophase < 0)
return -1;
}
if (eventsObj) {
events = PyObject_IsTrue(eventsObj);
if (events < 0)
return -1;
}
// set up the environment
if (pool)
self->environment = Environment_Clone(pool->environment);
else self->environment = Environment_NewFromScratch(threaded, events,
encoding, nencoding);
if (!self->environment)
return -1;
// keep a copy of the credentials
Py_XINCREF(usernameObj);
self->username = usernameObj;
Py_XINCREF(dsnObj);
self->dsn = dsnObj;
// perform some parsing, if necessary
if (Connection_SplitComponent(&self->username, &passwordObj, "/") < 0)
return -1;
if (Connection_SplitComponent(&passwordObj, &self->dsn, "@") < 0)
return -1;
// handle the different ways of initializing the connection
if (handle)
return Connection_Attach(self, handle);
if (pool || cclassObj)
return Connection_GetConnection(self, pool, passwordObj, cclassObj,
purity);
return Connection_Connect(self, connectMode, twophase, passwordObj,
newPasswordObj, moduleObj, actionObj, clientinfoObj,
editionObj);
}
//-----------------------------------------------------------------------------
// Connection_Free()
// Deallocate the connection, disconnecting from the database if necessary.
//-----------------------------------------------------------------------------
static void Connection_Free(
udt_Connection *self) // connection object
{
if (self->release) {
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
} else if (!self->attached) {
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_CLEAR(self->environment);
Py_CLEAR(self->sessionPool);
Py_CLEAR(self->username);
Py_CLEAR(self->dsn);
Py_CLEAR(self->version);
Py_CLEAR(self->inputTypeHandler);
Py_CLEAR(self->outputTypeHandler);
Py_TYPE(self)->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, *format, *formatArgs = NULL;
if (GetModuleAndName(Py_TYPE(connection), &module, &name) < 0)
return NULL;
if (connection->username && connection->username != Py_None &&
connection->dsn && connection->dsn != Py_None) {
format = cxString_FromAscii("<%s.%s to %s@%s>");
if (format)
formatArgs = PyTuple_Pack(4, module, name, connection->username,
connection->dsn);
} else if (connection->username && connection->username != Py_None) {
format = cxString_FromAscii("<%s.%s to user %s@local>");
if (format)
formatArgs = PyTuple_Pack(3, module, name, connection->username);
} else {
format = cxString_FromAscii("<%s.%s to externally identified user>");
if (format)
formatArgs = PyTuple_Pack(2, module, name);
}
Py_DECREF(module);
Py_DECREF(name);
if (!format)
return NULL;
if (!formatArgs) {
Py_DECREF(format);
return NULL;
}
result = cxString_Format(format, formatArgs);
Py_DECREF(format);
Py_DECREF(formatArgs);
return result;
}
//-----------------------------------------------------------------------------
// Connection_GetStmtCacheSize()
// Return the Oracle statement cache size.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetStmtCacheSize(
udt_Connection* self, // connection object
void* arg) // optional argument (ignored)
{
ub4 cacheSize;
sword status;
if (Connection_IsConnected(self) < 0)
return NULL;
status = OCIAttrGet(self->handle, OCI_HTYPE_SVCCTX,
(dvoid**) &cacheSize, 0, OCI_ATTR_STMTCACHESIZE,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_GetStmtCacheSize()") < 0)
return NULL;
return PyInt_FromLong(cacheSize);
}
//-----------------------------------------------------------------------------
// Connection_SetStmtCacheSize()
// Set the Oracle statement cache size.
//-----------------------------------------------------------------------------
static int Connection_SetStmtCacheSize(
udt_Connection* self, // connection object
PyObject *value, // value to set it to
void* arg) // optional argument (ignored)
{
ub4 valueToSet;
sword status;
if (Connection_IsConnected(self) < 0)
return -1;
if (!PyInt_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
valueToSet = (ub4) PyInt_AsLong(value);
if (PyErr_Occurred())
return -1;
status = OCIAttrSet(self->handle, OCI_HTYPE_SVCCTX, (dvoid*) &valueToSet,
0, OCI_ATTR_STMTCACHESIZE, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_SetStmtCacheSize()") < 0)
return -1;
return 0;
}
//-----------------------------------------------------------------------------
// Connection_GetType()
// Return a type object given its name.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetType(
udt_Connection *self, // connection
PyObject *args) // arguments
{
PyObject *nameObj = NULL;
// parse the arguments
if (!PyArg_ParseTuple(args, "O", &nameObj))
return NULL;
return (PyObject*) ObjectType_NewByName(self, nameObj);
}
//-----------------------------------------------------------------------------
// 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)
{
PyObject *procName, *listOfArguments;
udt_Variable *versionVar, *compatVar;
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, NULL);
if (!cursor)
return NULL;
// allocate version variable
versionVar = Variable_New(cursor, cursor->arraySize, &vt_String,
vt_String.size);
if (!versionVar) {
Py_DECREF(cursor);
return NULL;
}
// allocate compatibility variable
compatVar = Variable_New(cursor, cursor->arraySize, &vt_String,
vt_String.size);
if (!compatVar) {
Py_DECREF(versionVar);
Py_DECREF(cursor);
return NULL;
}
// create the list of arguments
listOfArguments = PyList_New(2);
if (!listOfArguments) {
Py_DECREF(versionVar);
Py_DECREF(compatVar);
Py_DECREF(cursor);
return NULL;
}
PyList_SET_ITEM(listOfArguments, 0, (PyObject*) versionVar);
PyList_SET_ITEM(listOfArguments, 1, (PyObject*) compatVar);
// create the string variable
procName = cxString_FromAscii("dbms_utility.db_version");
if (!procName) {
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
return NULL;
}
// call stored procedure
if (Cursor_Call(cursor, NULL, procName, listOfArguments, NULL) < 0) {
Py_DECREF(procName);
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
return NULL;
}
Py_DECREF(procName);
// retrieve value
self->version = Variable_GetValue(versionVar, 0);
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
Py_XINCREF(self->version);
return self->version;
}
//-----------------------------------------------------------------------------
// Connection_GetEncoding()
// Return the encoding associated with the environment of the connection.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetEncoding(
udt_Connection *self, // connection object
void *arg) // optional argument (ignored)
{
return cxString_FromAscii(self->environment->encoding);
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
//-----------------------------------------------------------------------------
// Connection_GetLTXID()
// Return the logical transaction id used with Transaction Guard.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetLTXID(
udt_Connection *self, // connection object
void *arg) // optional argument (ignored)
{
OCISession *sessionHandle;
ub4 ltxidLength;
sword status;
ub1 *ltxid;
// make sure connection is connected
if (Connection_IsConnected(self) < 0)
return NULL;
// acquire the session handle
status = OCIAttrGet(self->handle, OCI_HTYPE_SVCCTX,
(dvoid**) &sessionHandle, 0, OCI_ATTR_SESSION,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_GetLTXID(): determine session handle") < 0)
return NULL;
// get the value from the OCI
status = OCIAttrGet(sessionHandle, OCI_HTYPE_SESSION,
&ltxid, &ltxidLength, OCI_ATTR_LTXID,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_GetLTXID()") < 0)
return NULL;
return PyBytes_FromStringAndSize( (char*) ltxid, (Py_ssize_t) ltxidLength);
}
#endif
//-----------------------------------------------------------------------------
// Connection_GetNationalEncoding()
// Return the national encoding associated with the environment of the
// connection.
//-----------------------------------------------------------------------------
static PyObject *Connection_GetNationalEncoding(
udt_Connection *self, // connection object
void *arg) // optional argument (ignored)
{
return cxString_FromAscii(self->environment->nencoding);
}
//-----------------------------------------------------------------------------
// 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->release) {
Py_BEGIN_ALLOW_THREADS
status = OCISessionRelease(self->handle,
self->environment->errorHandle, NULL, 0, OCI_DEFAULT);
Py_END_ALLOW_THREADS
if (Environment_CheckForError(self->environment, status,
"Connection_Close(): release session") < 0)
return NULL;
self->release = 0;
}
else {
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->sessionHandle, OCI_HTYPE_SESSION);
self->sessionHandle = NULL;
OCIHandleFree(self->handle, OCI_HTYPE_SVCCTX);
}
if (self->serverHandle) {
status = OCIServerDetach(self->serverHandle,
self->environment->errorHandle, OCI_DEFAULT);
if (Environment_CheckForError(self->environment, status,
"Connection_Close(): server detach") < 0)
return NULL;
OCIHandleFree(self->serverHandle, OCI_HTYPE_SERVER);
self->serverHandle = NULL;
}
}
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;
self->commitMode = OCI_DEFAULT;
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;
// determine if a transaction handle was previously allocated
status = OCIAttrGet(self->handle, OCI_HTYPE_SVCCTX,
(dvoid**) &transactionHandle, 0, OCI_ATTR_TRANS,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_Begin(): find existing transaction handle") < 0)
return NULL;
// create a new transaction handle, if necessary
if (!transactionHandle) {
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;
// if nothing available to prepare, return False in order to allow for
// avoiding the call to commit() which will fail with ORA-24756
// (transaction does not exist)
if (status == OCI_SUCCESS_WITH_INFO) {
Py_INCREF(Py_False);
return Py_False;
}
self->commitMode = OCI_TRANS_TWOPHASE;
Py_INCREF(Py_True);
return Py_True;
}
//-----------------------------------------------------------------------------
// 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 *keywordArgs) // keyword arguments
{
PyObject *createArgs, *result;
Py_ssize_t numArgs = 0, i;
if (args)
numArgs = PyTuple_GET_SIZE(args);
createArgs = PyTuple_New(1 + numArgs);
if (!createArgs)
return NULL;
Py_INCREF(self);
PyTuple_SET_ITEM(createArgs, 0, (PyObject*) self);
for (i = 0; i < numArgs; i++)
PyTuple_SET_ITEM(createArgs, i + 1, PyTuple_GET_ITEM(args, i));
result = PyObject_Call( (PyObject*) &g_CursorType, createArgs,
keywordArgs);
Py_DECREF(createArgs);
return result;
}
//-----------------------------------------------------------------------------
// 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_NewEnqueueOptions()
// Creates a new enqueue options object and returns it.
//-----------------------------------------------------------------------------
static PyObject *Connection_NewEnqueueOptions(
udt_Connection *self, // connection
PyObject *args) // none
{
return (PyObject*) EnqOptions_New(self->environment);
}
//-----------------------------------------------------------------------------
// Connection_NewDequeueOptions()
// Creates a new dequeue options object and returns it.
//-----------------------------------------------------------------------------
static PyObject *Connection_NewDequeueOptions(
udt_Connection *self, // connection
PyObject *args) // none
{
return (PyObject*) DeqOptions_New(self->environment);
}
//-----------------------------------------------------------------------------
// Connection_NewMessageProperties()
// Creates a new message properties object and returns it.
//-----------------------------------------------------------------------------
static PyObject *Connection_NewMessageProperties(
udt_Connection *self, // connection
PyObject *args) // none
{
return (PyObject*) MessageProperties_New(self->environment);
}
//-----------------------------------------------------------------------------
// Connection_Dequeue()
// Dequeues a message using Advanced Queuing capabilities. The message ID is
// returned if a message is available or None if no message is available.
//-----------------------------------------------------------------------------
static PyObject *Connection_Dequeue(
udt_Connection *self, // connection
PyObject* args, // arguments
PyObject* keywordArgs) // keyword arguments
{
static char *keywordList[] = { "name", "options", "msgproperties",
"payload", NULL };
PyObject *nameObj, *excType, *excValue, *traceback;
udt_MessageProperties *propertiesObj;
udt_DeqOptions *optionsObj;
udt_Object *payloadObj;
udt_Buffer nameBuffer;
char *messageIdValue;
OCIRaw *messageId;
ub4 messageIdSize;
udt_Error *error;
sword status;
// parse arguments
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
&nameObj, &g_DeqOptionsType, &optionsObj, &g_MessagePropertiesType,
&propertiesObj, &g_ObjectType, &payloadObj))
return NULL;
if (cxBuffer_FromObject(&nameBuffer, nameObj,
self->environment->encoding) < 0)
return NULL;
// enqueue payload
messageId = NULL;
status = OCIAQDeq(self->handle, self->environment->errorHandle,
(oratext*) nameBuffer.ptr, optionsObj->handle,
propertiesObj->handle, payloadObj->objectType->tdo,
&payloadObj->instance, &payloadObj->indicator, &messageId,
OCI_DEFAULT);
cxBuffer_Clear(&nameBuffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Dequeue()") < 0) {
PyErr_Fetch(&excType, &excValue, &traceback);
if (excValue) {
error = (udt_Error*) excValue;
if (error->code == 25228) {
Py_XDECREF(excType);
Py_XDECREF(excValue);
Py_XDECREF(traceback);
Py_RETURN_NONE;
}
}
PyErr_Restore(excType, excValue, traceback);
return NULL;
}
// determine the message id
messageIdValue = (char*) OCIRawPtr(self->environment->handle, messageId);
messageIdSize = OCIRawSize(self->environment->handle, messageId);
return PyBytes_FromStringAndSize(messageIdValue, messageIdSize);
}
//-----------------------------------------------------------------------------
// Connection_Enqueue()
// Enqueues a message using Advanced Queuing capabilities. The message ID is
// returned.
//-----------------------------------------------------------------------------
static PyObject *Connection_Enqueue(
udt_Connection *self, // connection
PyObject* args, // arguments
PyObject* keywordArgs) // keyword arguments
{
static char *keywordList[] = { "name", "options", "msgproperties",
"payload", NULL };
udt_MessageProperties *propertiesObj;
udt_EnqOptions *optionsObj;
udt_Object *payloadObj;
udt_Buffer nameBuffer;
char *messageIdValue;
PyObject *nameObj;
OCIRaw *messageId;
ub4 messageIdSize;
sword status;
// parse arguments
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "OO!O!O!", keywordList,
&nameObj, &g_EnqOptionsType, &optionsObj, &g_MessagePropertiesType,
&propertiesObj, &g_ObjectType, &payloadObj))
return NULL;
if (cxBuffer_FromObject(&nameBuffer, nameObj,
self->environment->encoding) < 0)
return NULL;
// enqueue payload
messageId = NULL;
status = OCIAQEnq(self->handle, self->environment->errorHandle,
(oratext*) nameBuffer.ptr, optionsObj->handle,
propertiesObj->handle, payloadObj->objectType->tdo,
&payloadObj->instance, &payloadObj->indicator, &messageId,
OCI_DEFAULT);
cxBuffer_Clear(&nameBuffer);
if (Environment_CheckForError(self->environment, status,
"Connection_Enqueue()") < 0)
return NULL;
// determine the message id
messageIdValue = (char*) OCIRawPtr(self->environment->handle, messageId);
messageIdSize = OCIRawSize(self->environment->handle, messageId);
return PyBytes_FromStringAndSize(messageIdValue, messageIdSize);
}
//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
// Connection_ContextManagerEnter()
// Called when the connection is used as a context manager and simply returns
// itself as a convenience to the caller.
//-----------------------------------------------------------------------------
static PyObject *Connection_ContextManagerEnter(
udt_Connection *self, // connection
PyObject* args) // arguments
{
Py_INCREF(self);
return (PyObject*) self;
}
//-----------------------------------------------------------------------------
// Connection_ContextManagerExit()
// Called when the connection is used as a context manager and if any
// exception a rollback takes place; otherwise, a commit takes place.
//-----------------------------------------------------------------------------
static PyObject *Connection_ContextManagerExit(
udt_Connection *self, // connection
PyObject* args) // arguments
{
PyObject *excType, *excValue, *excTraceback, *result;
char *methodName;
if (!PyArg_ParseTuple(args, "OOO", &excType, &excValue, &excTraceback))
return NULL;
if (excType == Py_None && excValue == Py_None && excTraceback == Py_None)
methodName = "commit";
else methodName = "rollback";
result = PyObject_CallMethod((PyObject*) self, methodName, "");
if (!result)
return NULL;
Py_DECREF(result);
Py_INCREF(Py_False);
return Py_False;
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2)
#if !defined(AIX5) || ORACLE_VERSION_HEX >= ORACLE_VERSION(11, 1)
//-----------------------------------------------------------------------------
// Connection_Ping()
// Makes a round trip call to the server to confirm that the connection and
// server are active.
//-----------------------------------------------------------------------------
static PyObject *Connection_Ping(
udt_Connection *self, // connection
PyObject* args) // arguments
{
sword status;
if (Connection_IsConnected(self) < 0)
return NULL;
status = OCIPing(self->handle, self->environment->errorHandle,
OCI_DEFAULT);
if (Environment_CheckForError(self->environment, status,
"Connection_Ping()") < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
#endif
//-----------------------------------------------------------------------------
// Connection_Shutdown()
// Shuts down the database. Note that this must be done in two phases except
// in the situation where the instance is aborted.
//-----------------------------------------------------------------------------
static PyObject *Connection_Shutdown(
udt_Connection *self, // connection
PyObject* args, // arguments
PyObject* keywordArgs) // keyword arguments
{
static char *keywordList[] = { "mode", NULL };
sword status;
ub4 mode;
// parse arguments
mode = OCI_DEFAULT;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
&mode))
return NULL;
// perform the work
if (Connection_IsConnected(self) < 0)
return NULL;
status = OCIDBShutdown(self->handle, self->environment->errorHandle, NULL,
mode);
if (Environment_CheckForError(self->environment, status,
"Connection_Shutdown()") < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
//-----------------------------------------------------------------------------
// Connection_Startup()
// Starts up the database, equivalent to "startup nomount" in SQL*Plus.
//-----------------------------------------------------------------------------
static PyObject *Connection_Startup(
udt_Connection *self, // connection
PyObject* args, // arguments
PyObject* keywordArgs) // keyword arguments
{
static char *keywordList[] = { "force", "restrict", NULL };
PyObject *forceObj, *restrictObj;
int flagTemp;
sword status;
ub4 flags;
// parse arguments
forceObj = restrictObj = NULL;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|OO", keywordList,
&forceObj, &restrictObj))
return NULL;
// set the flags to use during startup
flags = 0;
if (forceObj) {
flagTemp = PyObject_IsTrue(forceObj);
if (flagTemp < 0)
return NULL;
if (flagTemp)
flags |= OCI_DBSTARTUPFLAG_FORCE;
}
if (restrictObj) {
flagTemp = PyObject_IsTrue(restrictObj);
if (flagTemp < 0)
return NULL;
if (flagTemp)
flags |= OCI_DBSTARTUPFLAG_RESTRICT;
}
// perform the work
if (Connection_IsConnected(self) < 0)
return NULL;
status = OCIDBStartup(self->handle, self->environment->errorHandle, NULL,
OCI_DEFAULT, flags);
if (Environment_CheckForError(self->environment, status,
"Connection_Startup()") < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
//-----------------------------------------------------------------------------
// Connection_Subscribe()
// Create a subscription to events that take place in the database.
//-----------------------------------------------------------------------------
static PyObject *Connection_Subscribe(
udt_Connection *self, // connection
PyObject* args, // arguments
PyObject* keywordArgs) // keyword arguments
{
static char *keywordList[] = { "namespace", "protocol", "callback",
"timeout", "operations", "rowids", "port", "qos", "cqqos", NULL };
ub4 namespace, protocol, port, timeout, rowids, operations, qos, cqqos;
PyObject *rowidsObj, *callback;
int temp;
// parse arguments
timeout = rowids = port = qos = cqqos = 0;
rowidsObj = callback = NULL;
namespace = OCI_SUBSCR_NAMESPACE_DBCHANGE;
protocol = OCI_SUBSCR_PROTO_OCI;
operations = OCI_OPCODE_ALLOPS;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|iiOiiOiii", keywordList,
&namespace, &protocol, &callback, &timeout, &operations,
&rowidsObj, &port, &qos, &cqqos))
return NULL;
// set the value for rowids
if (rowidsObj) {
temp = PyObject_IsTrue(rowidsObj);
if (temp < 0)
return NULL;
if (temp)
rowids = 1;
}
return (PyObject*) Subscription_New(self, namespace, protocol, port,
callback, timeout, operations, qos, cqqos, rowids);
}
#endif