Added support for a session callback (written in either PL/SQL or Pyt… · datamk/python-cx_Oracle@ed88224 · GitHub
Skip to content

Commit ed88224

Browse files
Added support for a session callback (written in either PL/SQL or Python)
which will be called when the actual tag assigned to a session doesn't match the tag requested.
1 parent b13c998 commit ed88224

6 files changed

Lines changed: 131 additions & 29 deletions

File tree

doc/src/connection.rst

Lines changed: 20 additions & 0 deletions

doc/src/module.rst

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ Module Interface
170170
increment=1, connectiontype=cx_Oracle.Connection, threaded=False, \
171171
getmode=cx_Oracle.SPOOL_ATTRVAL_NOWAIT, events=False, \
172172
homogeneous=True, externalauth=False, encoding=None, nencoding=None, \
173-
edition=None, timeout=0, waitTimeout=0, maxLifetimeSession=0)
173+
edition=None, timeout=0, waitTimeout=0, maxLifetimeSession=0, \
174+
sessionCallback=None)
174175

175176
Create and return a :ref:`session pool object <sesspool>`. This
176177
allows for very fast connections to the database and is of primary use in a
@@ -226,6 +227,19 @@ Module Interface
226227
which means that there is no maximum length of time that a pooled session
227228
may exist.
228229

230+
The sessionCallback parameter is expected to be either a string or a
231+
callable. If the parameter is a string, this refers to a PL/SQL procedure
232+
that will be called when :func:`SessionPool.acquire()` requests a tag and
233+
that tag does not match the connection's actual tag. Support for the PL/SQL
234+
procedure requires Oracle Client libraries 12.2 or later. See the
235+
`OCI documentation <https://www.oracle.com/pls/topic/lookup?
236+
ctx=dblatest&id=GUID-B853A020-752F-494A-8D88-D0396EF57177>`__ for more
237+
information. If the sessionCallback parameter is a callable, however, it
238+
will be called when a newly created connection is returned from the pool
239+
or when a tag is requested and that tag does not match the connection's
240+
actual tag. The callable will be invoked with the connection and the
241+
requested tag as its only parameters.
242+
229243
.. note::
230244

231245
This method is an extension to the DB API definition.

doc/src/session_pool.rst

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ SessionPool Object
2626
:data:`~cx_Oracle.ATTR_PURITY_NEW`, :data:`~cx_Oracle.ATTR_PURITY_SELF`, or
2727
:data:`~cx_Oracle.ATTR_PURITY_DEFAULT`.
2828

29-
The tag parameter, if specified, is expected to be a string and will limit
30-
the sessions that can be returned from a session pool unless the
31-
matchanytag parameter is set to True. In that case sessions with the
32-
specified tag will be preferred over others, but if no such sessions are
33-
available a session with a different tag may be returned instead. In any
34-
case, untagged sessions will always be returned if no sessions with the
35-
specified tag are available. Sessions are tagged when they are
36-
:meth:`released <SessionPool.release>` back to the pool.
29+
The tag parameter, if specified, is expected to be a string with name=value
30+
pairs like "k1=v1;k2=v2" and will limit the sessions that can be returned
31+
from a session pool unless the matchanytag parameter is set to True. In
32+
that case sessions with the specified tag will be preferred over others,
33+
but if no such sessions are available a session with a different tag may be
34+
returned instead. In any case, untagged sessions will always be returned if
35+
no sessions with the specified tag are available. Sessions are tagged when
36+
they are :meth:`released <SessionPool.release>` back to the pool.
3737

3838
The shardingkey and supershardingkey parameters, if specified, are expected
3939
to be a sequence of values which will be used to identify the database
@@ -136,6 +136,12 @@ SessionPool Object
136136
connections back to the pool in order to ensure sufficient resources are
137137
available.
138138

139+
If the tag is not None, it is expected to be a string with name=value pairs
140+
like "k1=v1;k2=v2" and will override the value in the property
141+
:attr:`Connection.tag`. If either :attr:`Connection.tag` or the tag
142+
parameter are not None, the connection will be retagged when it is released
143+
back to the pool.
144+
139145

140146
.. attribute:: SessionPool.stmtcachesize
141147

src/cxoConnection.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ static PyMemberDef cxoConnectionMembers[] = {
122122
{ "username", T_OBJECT, offsetof(cxoConnection, username), READONLY },
123123
{ "dsn", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
124124
{ "tnsentry", T_OBJECT, offsetof(cxoConnection, dsn), READONLY },
125+
{ "tag", T_OBJECT, offsetof(cxoConnection, tag), 0 },
125126
{ "autocommit", T_INT, offsetof(cxoConnection, autocommit), 0 },
126127
{ "inputtypehandler", T_OBJECT,
127128
offsetof(cxoConnection, inputTypeHandler), 0 },
@@ -681,14 +682,14 @@ static int cxoConnection_init(cxoConnection *conn, PyObject *args,
681682
{
682683
PyObject *tagObj, *matchAnyTagObj, *threadedObj, *eventsObj, *contextObj;
683684
PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj;
684-
PyObject *shardingKeyObj, *superShardingKeyObj;
685+
PyObject *shardingKeyObj, *superShardingKeyObj, *tempObj;
686+
int status, temp, invokeSessionCallback;
685687
dpiCommonCreateParams dpiCommonParams;
686688
dpiConnCreateParams dpiCreateParams;
687689
unsigned long long externalHandle;
688690
cxoConnectionParams params;
689691
PyObject *newPasswordObj;
690692
cxoSessionPool *pool;
691-
int status, temp;
692693

693694
// define keyword arguments
694695
static char *keywordList[] = { "user", "password", "dsn", "mode",
@@ -698,9 +699,10 @@ static int cxoConnection_init(cxoConnection *conn, PyObject *args,
698699

699700
// parse arguments
700701
pool = NULL;
702+
tagObj = Py_None;
701703
externalHandle = 0;
704+
passwordObj = dsnObj = cclassObj = editionObj = NULL;
702705
threadedObj = eventsObj = newPasswordObj = usernameObj = NULL;
703-
passwordObj = dsnObj = cclassObj = editionObj = tagObj = NULL;
704706
matchAnyTagObj = contextObj = shardingKeyObj = superShardingKeyObj = NULL;
705707
if (cxoUtils_initializeDPI() < 0)
706708
return -1;
@@ -812,9 +814,22 @@ static int cxoConnection_init(cxoConnection *conn, PyObject *args,
812814
params.dsnBuffer.size, &dpiCommonParams, &dpiCreateParams,
813815
&conn->handle);
814816
Py_END_ALLOW_THREADS
815-
cxoConnectionParams_finalize(&params);
816-
if (status < 0)
817+
if (status < 0) {
818+
cxoConnectionParams_finalize(&params);
817819
return cxoError_raiseAndReturnInt();
820+
}
821+
822+
// determine if session callback should be invoked; this takes place if
823+
// the connection is newly created by the pool or if the requested tag
824+
// does not match the actal tag
825+
invokeSessionCallback = 0;
826+
if (dpiCreateParams.outNewSession ||
827+
dpiCreateParams.outTagLength != params.tagBuffer.size ||
828+
(dpiCreateParams.outTagLength > 0 &&
829+
strncmp(dpiCreateParams.outTag, params.tagBuffer.ptr,
830+
dpiCreateParams.outTagLength) != 0))
831+
invokeSessionCallback = 1;
832+
cxoConnectionParams_finalize(&params);
818833

819834
// determine encodings to use
820835
if (pool)
@@ -828,6 +843,25 @@ static int cxoConnection_init(cxoConnection *conn, PyObject *args,
828843
cxoUtils_getAdjustedEncoding(conn->encodingInfo.nencoding);
829844
}
830845

846+
// set tag property
847+
if (dpiCreateParams.outTagLength > 0) {
848+
conn->tag = cxoPyString_fromEncodedString(dpiCreateParams.outTag,
849+
dpiCreateParams.outTagLength, conn->encodingInfo.encoding,
850+
NULL);
851+
if (!conn->tag)
852+
return -1;
853+
}
854+
855+
// invoke the session callback if applicable
856+
if (invokeSessionCallback && pool && pool->sessionCallback &&
857+
PyCallable_Check(pool->sessionCallback)) {
858+
tempObj = PyObject_CallFunctionObjArgs(pool->sessionCallback,
859+
(PyObject*) conn, tagObj, NULL);
860+
if (!tempObj)
861+
return -1;
862+
Py_DECREF(tempObj);
863+
}
864+
831865
return 0;
832866
}
833867

@@ -1106,12 +1140,21 @@ static PyObject *cxoConnection_getMaxBytesPerCharacter(cxoConnection *conn,
11061140
//-----------------------------------------------------------------------------
11071141
static PyObject *cxoConnection_close(cxoConnection *conn, PyObject *args)
11081142
{
1143+
cxoBuffer tagBuffer;
1144+
uint32_t mode;
11091145
int status;
11101146

11111147
if (cxoConnection_isConnected(conn) < 0)
11121148
return NULL;
1149+
if (cxoBuffer_fromObject(&tagBuffer, conn->tag,
1150+
conn->encodingInfo.encoding) < 0)
1151+
return NULL;
1152+
mode = DPI_MODE_CONN_CLOSE_DEFAULT;
1153+
if (conn->tag && conn->tag != Py_None)
1154+
mode |= DPI_MODE_CONN_CLOSE_RETAG;
11131155
Py_BEGIN_ALLOW_THREADS
1114-
status = dpiConn_close(conn->handle, DPI_MODE_CONN_CLOSE_DEFAULT, NULL, 0);
1156+
status = dpiConn_close(conn->handle, mode, (char*) tagBuffer.ptr,
1157+
tagBuffer.size);
11151158
Py_END_ALLOW_THREADS
11161159
if (status < 0)
11171160
return cxoError_raiseAndReturnNull();

src/cxoModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ struct cxoConnection {
230230
PyObject *username;
231231
PyObject *dsn;
232232
PyObject *version;
233+
PyObject *tag;
233234
dpiEncodingInfo encodingInfo;
234235
int autocommit;
235236
};
@@ -363,6 +364,7 @@ struct cxoSessionPool {
363364
PyObject *username;
364365
PyObject *dsn;
365366
PyObject *name;
367+
PyObject *sessionCallback;
366368
PyTypeObject *connectionType;
367369
};
368370

src/cxoSessionPool.c

Lines changed: 31 additions & 14 deletions

0 commit comments

Comments
 (0)