1709 lines
62 KiB
C
1709 lines
62 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 *password;
|
|
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;
|
|
#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
|
|
|
|
#ifdef ORACLE_10GR2
|
|
static ub4 gc_CurrentSchemaAttribute = OCI_ATTR_CURRENT_SCHEMA;
|
|
#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*);
|
|
static PyObject *Connection_ContextManagerEnter(udt_Connection*, PyObject*);
|
|
static PyObject *Connection_ContextManagerExit(udt_Connection*, PyObject*);
|
|
static PyObject *Connection_ChangePasswordExternal(udt_Connection*, PyObject*);
|
|
#ifndef WITH_UNICODE
|
|
static PyObject *Connection_GetEncoding(udt_Connection*, void*);
|
|
static PyObject *Connection_GetNationalEncoding(udt_Connection*, void*);
|
|
#endif
|
|
static PyObject *Connection_GetStmtCacheSize(udt_Connection*, void*);
|
|
static int Connection_SetStmtCacheSize(udt_Connection*, PyObject*, void*);
|
|
#ifdef ORACLE_10G
|
|
static PyObject *Connection_GetOCIAttr(udt_Connection*, ub4*);
|
|
#endif
|
|
static int Connection_SetOCIAttr(udt_Connection*, PyObject*, ub4*);
|
|
#ifdef ORACLE_10GR2
|
|
#if !defined(AIX5) || defined(ORACLE_11g)
|
|
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
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 },
|
|
{ "__enter__", (PyCFunction) Connection_ContextManagerEnter, METH_NOARGS },
|
|
{ "__exit__", (PyCFunction) Connection_ContextManagerExit, METH_VARARGS },
|
|
#ifdef ORACLE_10GR2
|
|
#if !defined(AIX5) || defined(ORACLE_11g)
|
|
{ "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 },
|
|
{ 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), 0 },
|
|
{ "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[] = {
|
|
#ifndef WITH_UNICODE
|
|
{ "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 },
|
|
{ "stmtcachesize", (getter) Connection_GetStmtCacheSize,
|
|
(setter) Connection_SetStmtCacheSize, 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
|
|
{ "client_identifier", 0, (setter) Connection_SetOCIAttr, 0,
|
|
&gc_ClientIdentifierAttribute },
|
|
#ifdef ORACLE_10GR2
|
|
{ "current_schema", (getter) Connection_GetOCIAttr,
|
|
(setter) Connection_SetOCIAttr, 0, &gc_CurrentSchemaAttribute },
|
|
#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 *cclassObj, // connection class (DRCP)
|
|
ub4 purity) // purity (DRCP)
|
|
{
|
|
int externalCredentials, proxyCredentials;
|
|
udt_Environment *environment;
|
|
udt_StringBuffer buffer;
|
|
OCIAuthInfo *authInfo;
|
|
PyObject *dbNameObj;
|
|
boolean found;
|
|
sword status;
|
|
ub4 mode;
|
|
|
|
// set things up for the call to acquire a session
|
|
authInfo = NULL;
|
|
proxyCredentials = 0;
|
|
if (pool) {
|
|
environment = pool->environment;
|
|
dbNameObj = pool->name;
|
|
mode = OCI_SESSGET_SPOOL;
|
|
if (!pool->homogeneous && pool->username && self->username) {
|
|
proxyCredentials = PyObject_RichCompareBool(self->username,
|
|
pool->username, Py_NE);
|
|
if (proxyCredentials < 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 || proxyCredentials) {
|
|
|
|
// 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
|
|
externalCredentials = 1;
|
|
if (StringBuffer_Fill(&buffer, self->username) < 0)
|
|
return -1;
|
|
if (buffer.size > 0) {
|
|
externalCredentials = 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) {
|
|
StringBuffer_Clear(&buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
StringBuffer_Clear(&buffer);
|
|
|
|
// set the password, if applicable
|
|
if (StringBuffer_Fill(&buffer, self->password) < 0)
|
|
return -1;
|
|
if (buffer.size > 0) {
|
|
externalCredentials = 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) {
|
|
StringBuffer_Clear(&buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
StringBuffer_Clear(&buffer);
|
|
|
|
// if no user name or password are set, using external credentials
|
|
if (!pool && externalCredentials)
|
|
mode |= OCI_SESSGET_CREDEXT;
|
|
|
|
#ifdef ORACLE_11G
|
|
// set the connection class, if applicable
|
|
if (StringBuffer_Fill(&buffer, cclassObj) < 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) {
|
|
StringBuffer_Clear(&buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
StringBuffer_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
|
|
}
|
|
|
|
// acquire the new session
|
|
if (StringBuffer_Fill(&buffer, dbNameObj) < 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
|
|
StringBuffer_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 (!proxyCredentials) {
|
|
Py_INCREF(pool->username);
|
|
self->username = pool->username;
|
|
Py_INCREF(pool->password);
|
|
self->password = pool->password;
|
|
}
|
|
Py_INCREF(pool->dsn);
|
|
self->dsn = pool->dsn;
|
|
Py_INCREF(pool);
|
|
self->sessionPool = pool;
|
|
}
|
|
|
|
self->release = 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef ORACLE_10G
|
|
//-----------------------------------------------------------------------------
|
|
// 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_StringBuffer buffer;
|
|
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, (ub4*) &buffer.size, *attribute,
|
|
self->environment->errorHandle);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Connection_GetOCIAttr()") < 0)
|
|
return NULL;
|
|
|
|
return cxString_FromEncodedString(buffer.ptr, buffer.size);
|
|
}
|
|
#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_StringBuffer buffer;
|
|
sword status;
|
|
|
|
// verify arguments
|
|
if (!cxString_Check(value)) {
|
|
PyErr_SetString(PyExc_TypeError, "value must be a string");
|
|
return -1;
|
|
}
|
|
|
|
// 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 (StringBuffer_Fill(&buffer, value))
|
|
return -1;
|
|
status = OCIAttrSet(sessionHandle, OCI_HTYPE_SESSION, (text*) buffer.ptr,
|
|
buffer.size, *attribute, self->environment->errorHandle);
|
|
StringBuffer_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_StringBuffer usernameBuffer, oldPasswordBuffer, newPasswordBuffer;
|
|
sword status;
|
|
|
|
if (StringBuffer_Fill(&usernameBuffer, self->username) < 0)
|
|
return -1;
|
|
if (StringBuffer_Fill(&oldPasswordBuffer, oldPasswordObj) < 0) {
|
|
StringBuffer_Clear(&usernameBuffer);
|
|
return -1;
|
|
}
|
|
if (StringBuffer_Fill(&newPasswordBuffer, newPasswordObj) < 0) {
|
|
StringBuffer_Clear(&usernameBuffer);
|
|
StringBuffer_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
|
|
StringBuffer_Clear(&usernameBuffer);
|
|
StringBuffer_Clear(&oldPasswordBuffer);
|
|
StringBuffer_Clear(&newPasswordBuffer);
|
|
if (Environment_CheckForError(self->environment, status,
|
|
"Connection_ChangePassword(): change password") < 0)
|
|
return -1;
|
|
|
|
Py_XDECREF(self->password);
|
|
Py_INCREF(newPasswordObj);
|
|
self->password = newPasswordObj;
|
|
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 *newPasswordObj) // new password (if desired)
|
|
{
|
|
ub4 credentialType = OCI_CRED_EXT;
|
|
udt_StringBuffer 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 (StringBuffer_Fill(&buffer, self->dsn) < 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
|
|
StringBuffer_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 (StringBuffer_Fill(&buffer, self->username) < 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) {
|
|
StringBuffer_Clear(&buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
StringBuffer_Clear(&buffer);
|
|
|
|
// set password in session handle
|
|
if (StringBuffer_Fill(&buffer, self->password) < 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) {
|
|
StringBuffer_Clear(&buffer);
|
|
return -1;
|
|
}
|
|
}
|
|
StringBuffer_Clear(&buffer);
|
|
|
|
#ifdef OCI_ATTR_DRIVER_NAME
|
|
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 a new password has been specified, change it which will also
|
|
// establish the session
|
|
if (newPasswordObj)
|
|
return Connection_ChangePassword(self, self->password, 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"
|
|
#ifdef ORACLE_10GR2
|
|
#include "Subscription.c"
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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_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 *threadedObj, *twophaseObj, *eventsObj, *newPasswordObj;
|
|
PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj;
|
|
int threaded, twophase, events;
|
|
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", NULL };
|
|
|
|
// parse arguments
|
|
pool = NULL;
|
|
handle = NULL;
|
|
connectMode = OCI_DEFAULT;
|
|
usernameObj = passwordObj = dsnObj = cclassObj = NULL;
|
|
threadedObj = twophaseObj = eventsObj = newPasswordObj = NULL;
|
|
threaded = twophase = events = purity = 0;
|
|
#ifdef ORACLE_11G
|
|
purity = OCI_ATTR_PURITY_DEFAULT;
|
|
#endif
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O!O!O!iiO!OOOO!iO!",
|
|
keywordList, cxString_Type, &usernameObj, cxString_Type,
|
|
&passwordObj, cxString_Type, &dsnObj, &connectMode, &handle,
|
|
&g_SessionPoolType, &pool, &threadedObj, &twophaseObj, &eventsObj,
|
|
cxString_Type, &cclassObj, &purity, cxString_Type,
|
|
&newPasswordObj))
|
|
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);
|
|
if (!self->environment)
|
|
return -1;
|
|
|
|
// keep a copy of the credentials
|
|
Py_XINCREF(usernameObj);
|
|
self->username = usernameObj;
|
|
Py_XINCREF(passwordObj);
|
|
self->password = passwordObj;
|
|
Py_XINCREF(dsnObj);
|
|
self->dsn = dsnObj;
|
|
|
|
// perform some parsing, if necessary
|
|
if (Connection_SplitComponent(&self->username, &self->password, "/") < 0)
|
|
return -1;
|
|
if (Connection_SplitComponent(&self->password, &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, cclassObj, purity);
|
|
return Connection_Connect(self, connectMode, twophase, newPasswordObj);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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->password);
|
|
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;
|
|
}
|
|
|
|
|
|
#ifndef WITH_UNICODE
|
|
//-----------------------------------------------------------------------------
|
|
// 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 charset 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_GetCharacterSetName(): get Oracle charset 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_GetCharacterSetName(): translate NLS charset") < 0)
|
|
return NULL;
|
|
|
|
return PyBytes_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_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_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);
|
|
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_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;
|
|
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 *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;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|
|
|
|
#ifdef ORACLE_10GR2
|
|
#if !defined(AIX5) || defined(ORACLE_11g)
|
|
//-----------------------------------------------------------------------------
|
|
// 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", NULL };
|
|
ub4 namespace, protocol, port, timeout, rowids, operations;
|
|
PyObject *rowidsObj, *callback;
|
|
int temp;
|
|
|
|
// parse arguments
|
|
timeout = rowids = port = 0;
|
|
rowidsObj = callback = NULL;
|
|
namespace = OCI_SUBSCR_NAMESPACE_DBCHANGE;
|
|
protocol = OCI_SUBSCR_PROTO_OCI;
|
|
operations = OCI_OPCODE_ALLOPS;
|
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|iiOiiOi", keywordList,
|
|
&namespace, &protocol, &callback, &timeout, &operations,
|
|
&rowidsObj, &port))
|
|
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, rowids);
|
|
}
|
|
#endif
|
|
|