python-cx_Oracle/SessionPool.c

534 lines
20 KiB
C

//-----------------------------------------------------------------------------
// SessionPool.c
// Handles session pooling (only available in Oracle 9i and up).
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// structure for the Python type "SessionPool"
//-----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD
OCISPool *handle;
ub4 minSessions;
ub4 maxSessions;
ub4 sessionIncrement;
ub4 cacheSize;
int homogeneous;
PyObject *name;
PyObject *username;
PyObject *password;
PyObject *dsn;
udt_Environment *environment;
PyTypeObject *connectionType;
} udt_SessionPool;
//-----------------------------------------------------------------------------
// constants for the OCI attributes
//-----------------------------------------------------------------------------
static ub4 gc_OpenAttribute = OCI_ATTR_SPOOL_OPEN_COUNT;
static ub4 gc_BusyAttribute = OCI_ATTR_SPOOL_BUSY_COUNT;
static ub4 gc_TimeoutAttribute = OCI_ATTR_SPOOL_TIMEOUT;
static ub4 gc_GetModeAttribute = OCI_ATTR_SPOOL_GETMODE;
//-----------------------------------------------------------------------------
// functions for the Python type "SessionPool"
//-----------------------------------------------------------------------------
static PyObject *SessionPool_New(PyTypeObject*, PyObject*, PyObject*);
static int SessionPool_Init(udt_SessionPool*, PyObject*, PyObject*);
static void SessionPool_Free(udt_SessionPool*);
static PyObject *SessionPool_Acquire(udt_SessionPool*, PyObject*, PyObject*);
static PyObject *SessionPool_Drop(udt_SessionPool*, PyObject*);
static PyObject *SessionPool_Release(udt_SessionPool*, PyObject*);
static PyObject *SessionPool_GetOCIAttr(udt_SessionPool*, ub4*);
static int SessionPool_SetOCIAttr(udt_SessionPool*, PyObject*, ub4*);
//-----------------------------------------------------------------------------
// declaration of methods for Python type "SessionPool"
//-----------------------------------------------------------------------------
static PyMethodDef g_SessionPoolMethods[] = {
{ "acquire", (PyCFunction) SessionPool_Acquire,
METH_VARARGS | METH_KEYWORDS },
{ "drop", (PyCFunction) SessionPool_Drop, METH_VARARGS },
{ "release", (PyCFunction) SessionPool_Release, METH_VARARGS },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of members for Python type "SessionPool"
//-----------------------------------------------------------------------------
static PyMemberDef g_SessionPoolMembers[] = {
{ "username", T_OBJECT, offsetof(udt_SessionPool, username), READONLY },
{ "password", T_OBJECT, offsetof(udt_SessionPool, password), 0 },
{ "dsn", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY },
{ "tnsentry", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY },
{ "name", T_OBJECT, offsetof(udt_SessionPool, name), READONLY },
{ "max", T_INT, offsetof(udt_SessionPool, maxSessions), READONLY },
{ "min", T_INT, offsetof(udt_SessionPool, minSessions), READONLY },
{ "increment", T_INT, offsetof(udt_SessionPool, sessionIncrement),
READONLY },
{ "homogeneous", T_INT, offsetof(udt_SessionPool, homogeneous), READONLY },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of calculated members for Python type "SessionPool"
//-----------------------------------------------------------------------------
static PyGetSetDef g_SessionPoolCalcMembers[] = {
{ "opened", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_OpenAttribute },
{ "busy", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_BusyAttribute },
{ "timeout", (getter) SessionPool_GetOCIAttr,
(setter) SessionPool_SetOCIAttr, 0, &gc_TimeoutAttribute },
{ "getmode", (getter) SessionPool_GetOCIAttr,
(setter) SessionPool_SetOCIAttr, 0, &gc_GetModeAttribute },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of Python type "SessionPool"
//-----------------------------------------------------------------------------
static PyTypeObject g_SessionPoolType = {
PyVarObject_HEAD_INIT(NULL, 0)
"OracleSessionPool", // tp_name
sizeof(udt_SessionPool), // tp_basicsize
0, // tp_itemsize
(destructor) SessionPool_Free, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
// tp_flags
0, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
g_SessionPoolMethods, // tp_methods
g_SessionPoolMembers, // tp_members
g_SessionPoolCalcMembers, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
(initproc) SessionPool_Init, // tp_init
0, // tp_alloc
(newfunc) SessionPool_New, // tp_new
0, // tp_free
0, // tp_is_gc
0 // tp_bases
};
#include "Connection.c"
//-----------------------------------------------------------------------------
// SessionPool_New()
// Create a new session pool object.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_New(
PyTypeObject *type, // type object
PyObject *args, // arguments
PyObject *keywordArgs) // keyword arguments
{
udt_SessionPool *newObject;
// create the object
newObject = (udt_SessionPool*) type->tp_alloc(type, 0);
if (!newObject)
return NULL;
newObject->environment = NULL;
return (PyObject*) newObject;
}
//-----------------------------------------------------------------------------
// SessionPool_Init()
// Initialize the session pool object.
//-----------------------------------------------------------------------------
static int SessionPool_Init(
udt_SessionPool *self, // session pool object
PyObject *args, // arguments
PyObject *keywordArgs) // keyword arguments
{
unsigned minSessions, maxSessions, sessionIncrement;
PyObject *threadedObj, *eventsObj, *homogeneousObj;
udt_Buffer username, password, dsn;
int threaded, events, homogeneous;
PyTypeObject *connectionType;
unsigned poolNameLength;
const char *poolName;
sword status;
ub4 poolMode;
ub1 getMode;
// define keyword arguments
static char *keywordList[] = { "user", "password", "dsn", "min", "max",
"increment", "connectiontype", "threaded", "getmode", "events",
"homogeneous", NULL };
// parse arguments and keywords
homogeneous = 1;
threaded = events = 0;
threadedObj = eventsObj = homogeneousObj = NULL;
connectionType = &g_ConnectionType;
getMode = OCI_SPOOL_ATTRVAL_NOWAIT;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!O!O!iii|OObOO",
keywordList, cxString_Type, &self->username,
cxString_Type, &self->password, cxString_Type, &self->dsn,
&minSessions, &maxSessions, &sessionIncrement, &connectionType,
&threadedObj, &getMode, &eventsObj, &homogeneousObj))
return -1;
if (!PyType_Check(connectionType)) {
PyErr_SetString(g_ProgrammingErrorException,
"connectiontype must be a type");
return -1;
}
if (!PyType_IsSubtype(connectionType, &g_ConnectionType)) {
PyErr_SetString(g_ProgrammingErrorException,
"connectiontype must be a subclass of Connection");
return -1;
}
if (threadedObj) {
threaded = PyObject_IsTrue(threadedObj);
if (threaded < 0)
return -1;
}
if (eventsObj) {
events = PyObject_IsTrue(eventsObj);
if (events < 0)
return -1;
}
if (homogeneousObj) {
homogeneous = PyObject_IsTrue(homogeneousObj);
if (homogeneous < 0)
return -1;
}
// initialize the object's members
Py_INCREF(connectionType);
self->connectionType = connectionType;
Py_INCREF(self->dsn);
Py_INCREF(self->username);
Py_INCREF(self->password);
self->minSessions = minSessions;
self->maxSessions = maxSessions;
self->sessionIncrement = sessionIncrement;
self->homogeneous = homogeneous;
// set up the environment
self->environment = Environment_NewFromScratch(threaded, events, NULL,
NULL);
if (!self->environment)
return -1;
// create the session pool handle
status = OCIHandleAlloc(self->environment->handle, (dvoid**) &self->handle,
OCI_HTYPE_SPOOL, 0, 0);
if (Environment_CheckForError(self->environment, status,
"SessionPool_New(): allocate handle") < 0)
return -1;
// prepare pool mode
poolMode = OCI_SPC_STMTCACHE;
if (self->homogeneous)
poolMode |= OCI_SPC_HOMOGENEOUS;
// create the session pool
if (cxBuffer_FromObject(&username, self->username,
self->environment->encoding) < 0)
return -1;
if (cxBuffer_FromObject(&password, self->password,
self->environment->encoding) < 0) {
cxBuffer_Clear(&username);
return -1;
}
if (cxBuffer_FromObject(&dsn, self->dsn,
self->environment->encoding) < 0) {
cxBuffer_Clear(&username);
cxBuffer_Clear(&password);
return -1;
}
Py_BEGIN_ALLOW_THREADS
status = OCISessionPoolCreate(self->environment->handle,
self->environment->errorHandle, self->handle,
(OraText**) &poolName, &poolNameLength, (OraText*) dsn.ptr,
dsn.size, minSessions, maxSessions, sessionIncrement,
(OraText*) username.ptr, username.size, (OraText*) password.ptr,
password.size, poolMode);
Py_END_ALLOW_THREADS
cxBuffer_Clear(&username);
cxBuffer_Clear(&password);
cxBuffer_Clear(&dsn);
if (Environment_CheckForError(self->environment, status,
"SessionPool_New(): create pool") < 0)
return -1;
// create the string for the pool name
self->name = cxString_FromEncodedString(poolName, poolNameLength,
self->environment->encoding);
if (!self->name)
return -1;
// set the mode on the pool
status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, (dvoid*) &getMode, 0,
OCI_ATTR_SPOOL_GETMODE, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"SessionPool_New(): set wait mode") < 0)
return -1;
return 0;
}
//-----------------------------------------------------------------------------
// SessionPool_Free()
// Deallocate the session pool.
//-----------------------------------------------------------------------------
static void SessionPool_Free(
udt_SessionPool *self) // session pool
{
if (self->handle) {
OCISessionPoolDestroy(self->handle, self->environment->errorHandle,
OCI_SPD_FORCE);
OCIHandleFree(self->handle, OCI_HTYPE_SPOOL);
}
Py_XDECREF(self->name);
Py_XDECREF(self->environment);
Py_XDECREF(self->username);
Py_XDECREF(self->password);
Py_XDECREF(self->dsn);
Py_TYPE(self)->tp_free((PyObject*) self);
}
//-----------------------------------------------------------------------------
// SessionPool_IsConnected()
// Determines if the session pool object is connected to the database. If
// not, a Python exception is raised.
//-----------------------------------------------------------------------------
static int SessionPool_IsConnected(
udt_SessionPool *self) // session pool
{
if (!self->handle) {
PyErr_SetString(g_InterfaceErrorException, "not connected");
return -1;
}
return 0;
}
//-----------------------------------------------------------------------------
// SessionPool_Acquire()
// Create a new connection within the session pool.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_Acquire(
udt_SessionPool *self, // session pool
PyObject *args, // arguments
PyObject *keywordArgs) // keyword arguments
{
static char *keywordList[] = { "user", "password", "cclass", "purity",
NULL };
PyObject *createKeywordArgs, *result, *cclassObj, *purityObj;
unsigned usernameLength, passwordLength;
char *username, *password;
// parse arguments
username = NULL;
password = NULL;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#OO", keywordList,
&username, &usernameLength, &password, &passwordLength, &cclassObj,
&purityObj))
return NULL;
if (self->homogeneous && (username || password)) {
PyErr_SetString(g_ProgrammingErrorException,
"pool is homogeneous. Proxy authentication is not possible.");
return NULL;
}
// make sure session pool is connected
if (SessionPool_IsConnected(self) < 0)
return NULL;
// create arguments
if (keywordArgs)
createKeywordArgs = PyDict_Copy(keywordArgs);
else createKeywordArgs = PyDict_New();
if (!createKeywordArgs)
return NULL;
if (PyDict_SetItemString(createKeywordArgs, "pool",
(PyObject*) self) < 0) {
Py_DECREF(createKeywordArgs);
return NULL;
}
// create the connection object
result = PyObject_Call( (PyObject*) self->connectionType, args,
createKeywordArgs);
Py_DECREF(createKeywordArgs);
return result;
}
//-----------------------------------------------------------------------------
// SessionPool_InternalRelease()
// Internal method used to release a connection back to the pool in order to
// allow for the possibility of dropping the connection.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_InternalRelease(
udt_SessionPool *self, // session pool
PyObject *args, // arguments
ub4 mode) // OCI mode to use
{
udt_Connection *connection;
sword status;
// connection is expected
if (!PyArg_ParseTuple(args, "O!", &g_ConnectionType, &connection))
return NULL;
// make sure session pool is connected
if (SessionPool_IsConnected(self) < 0)
return NULL;
if (connection->sessionPool != self) {
PyErr_SetString(g_ProgrammingErrorException,
"connection not acquired with this session pool");
return NULL;
}
// attempt a rollback but if dropping the connection from the pool
// ignore the error
Py_BEGIN_ALLOW_THREADS
status = OCITransRollback(connection->handle,
connection->environment->errorHandle, OCI_DEFAULT);
Py_END_ALLOW_THREADS
if (Environment_CheckForError(connection->environment, status,
"SessionPool_Release(): rollback") < 0) {
if (mode != OCI_SESSRLS_DROPSESS)
return NULL;
PyErr_Clear();
}
// release the connection
Py_BEGIN_ALLOW_THREADS
status = OCISessionRelease(connection->handle,
connection->environment->errorHandle, NULL, 0, mode);
Py_END_ALLOW_THREADS
if (Environment_CheckForError(connection->environment, status,
"SessionPool_Release(): release session") < 0)
return NULL;
// ensure that the connection behaves as closed
Py_DECREF(connection->sessionPool);
connection->sessionPool = NULL;
connection->handle = NULL;
Py_INCREF(Py_None);
return Py_None;
}
//-----------------------------------------------------------------------------
// SessionPool_Drop()
// Release a connection back to the session pool, dropping it so that a new
// connection will be created if needed.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_Drop(
udt_SessionPool *self, // session pool
PyObject *args) // arguments
{
return SessionPool_InternalRelease(self, args, OCI_SESSRLS_DROPSESS);
}
//-----------------------------------------------------------------------------
// SessionPool_Release()
// Release a connection back to the session pool.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_Release(
udt_SessionPool *self, // session pool
PyObject *args) // arguments
{
return SessionPool_InternalRelease(self, args, OCI_DEFAULT);
}
//-----------------------------------------------------------------------------
// SessionPool_GetOCIAttr()
// Return the value for the OCI attribute.
//-----------------------------------------------------------------------------
static PyObject *SessionPool_GetOCIAttr(
udt_SessionPool *self, // session pool
ub4 *attribute) // OCI attribute type
{
sword status;
ub4 value;
// make sure session pool is connected
if (SessionPool_IsConnected(self) < 0)
return NULL;
// get the value from the OCI
status = OCIAttrGet(self->handle, OCI_HTYPE_SPOOL, &value, 0, *attribute,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"SessionPool_GetOCIAttr()") < 0)
return NULL;
if (*attribute == OCI_ATTR_SPOOL_GETMODE)
return PyInt_FromLong((ub1) value);
return PyInt_FromLong(value);
}
//-----------------------------------------------------------------------------
// SessionPool_SetOCIAttr()
// Set the value of the OCI attribute.
//-----------------------------------------------------------------------------
static int SessionPool_SetOCIAttr(
udt_SessionPool *self, // session pool
PyObject *value, // value to set
ub4 *attribute) // OCI attribute type
{
ub4 ociValue;
sword status;
// make sure session pool is connected
if (SessionPool_IsConnected(self) < 0)
return -1;
// set the value in the OCI
if (!PyInt_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be an integer");
return -1;
}
ociValue = PyInt_AsLong(value);
if (PyErr_Occurred())
return -1;
status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, &ociValue, 0,
*attribute, self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"SessionPool_SetOCIAttr()") < 0)
return -1;
return 0;
}