Skip to content

Commit ff01f65

Browse files
Added support for using application context during the creation of a
connection. This should be used in preference to the module, action and clientinfo arguments which are now deprecated.
1 parent f909593 commit ff01f65

File tree

4 files changed

+164
-12
lines changed

4 files changed

+164
-12
lines changed

doc/module.rst

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ Module Interface
2323
available in Oracle 10g Release 2 and higher.
2424

2525

26-
.. function:: Connection([user, password, dsn, mode, handle, pool, threaded, twophase, events, cclass, purity, newpassword, encoding, nencoding, module, action, clientinfo, edition])
27-
connect([user, password, dsn, mode, handle, pool, threaded, twophase, events, cclass, purity, newpassword, encoding, nencoding, module, action, clientinfo, edition])
26+
.. function:: Connection([user, password, dsn, mode, handle, pool, threaded, twophase, events, cclass, purity, newpassword, encoding, nencoding, module, action, clientinfo, edition, appcontext])
27+
connect([user, password, dsn, mode, handle, pool, threaded, twophase, events, cclass, purity, newpassword, encoding, nencoding, module, action, clientinfo, edition, appcontext])
2828

2929
Constructor for creating a connection to the database. Return a Connection
3030
object (:ref:`connobj`). All arguments are optional and can be specified as
@@ -84,14 +84,24 @@ Module Interface
8484
The nencoding argument is expected to be a string if specified and sets the
8585
national encoding to use for national character set database strings.
8686

87-
The module, action and clientinfo arguments are expected to be strings if
88-
specified and sets the module, action and client_info attributes on the
89-
connection respectively.
87+
The module, action and clientinfo arguments are expected to be strings, if
88+
specified, and sets the module, action and client_info attributes on the
89+
connection respectively. These arguments are deprecated and will be removed
90+
in a future version of cx_Oracle since Oracle does not support their use
91+
during the establishing of a connection. Instead, application context (see
92+
below) should be used.
9093

9194
The edition argument is expected to be a string if specified and sets the
9295
edition to use for the session. It is only relevant if both the client and
9396
the server are at least Oracle Database 11.2.
9497

98+
The appcontext argument is expected to be a list of 3-tuples, if specified,
99+
and sets the application context for the connection. Application context
100+
is available in the database by using the sys_context() PL/SQL method and
101+
can be used within a logon trigger as well as any other PL/SQL procedures.
102+
Each entry in the list is expected to contain three strings: the namespace,
103+
the name and the value.
104+
95105

96106
.. function:: Cursor(connection)
97107

doc/releasenotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Version 5.3 (TBD)
5757
connection directly. The use of the twophase argument is now deprecated.
5858
Applications should set the internal_name and external_name attributes
5959
directly to a value appropriate to the application.
60+
28) Added support for using application context during the creation of a
61+
connection. This should be used in preference to the module, action and
62+
clientinfo arguments which are now deprecated.
6063

6164

6265
Version 5.2.1 (January 2016)

samples/AppContext.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#------------------------------------------------------------------------------
2+
# AppContext.py
3+
# This script demonstrates the use of application context. Application
4+
# context is available within logon triggers and can be retrieved by using the
5+
# function sys_context().
6+
#------------------------------------------------------------------------------
7+
8+
from __future__ import print_function
9+
10+
import cx_Oracle
11+
12+
# define constants used throughout the script; adjust as desired
13+
CONNECT_STRING = "cx_Oracle/dev@localhost/orcl"
14+
APP_CTX_NAMESPACE = "CLIENTCONTEXT"
15+
APP_CTX_ENTRIES = [
16+
( APP_CTX_NAMESPACE, "ATTR1", "VALUE1" ),
17+
( APP_CTX_NAMESPACE, "ATTR2", "VALUE2" ),
18+
( APP_CTX_NAMESPACE, "ATTR3", "VALUE3" )
19+
]
20+
21+
connection = cx_Oracle.Connection(CONNECT_STRING, appcontext = APP_CTX_ENTRIES)
22+
cursor = connection.cursor()
23+
for namespace, name, value in APP_CTX_ENTRIES:
24+
cursor.execute("select sys_context(:1, :2) from dual", (namespace, name))
25+
value, = cursor.fetchone()
26+
print("Value of context key", name, "is", value)
27+

src/Connection.c

Lines changed: 119 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,109 @@ static PyObject *Connection_ChangePasswordExternal(
607607
}
608608

609609

610+
//-----------------------------------------------------------------------------
611+
// Connection_ProcessAppContext()
612+
// Process application context during the establishing of a session. This is
613+
// intended to replace setting module, action and clientinfo (which was never
614+
// intended to be supported anyway!) and is more flexible in any case.
615+
//-----------------------------------------------------------------------------
616+
static int Connection_ProcessAppContext(
617+
udt_Connection *self, // connection
618+
PyObject *appContextObj) // app context value
619+
{
620+
void *listHandle, *entryHandle;
621+
PyObject *entryObj;
622+
udt_Buffer buffer;
623+
ub4 numEntries, i;
624+
sword status;
625+
626+
// validate context is a list with at least one entry in it
627+
if (!appContextObj)
628+
return 0;
629+
if (!PyList_Check(appContextObj)) {
630+
PyErr_SetString(PyExc_TypeError,
631+
"appcontext should be a list of 3-tuples");
632+
return -1;
633+
}
634+
numEntries = (ub4) PyList_GET_SIZE(appContextObj);
635+
if (numEntries == 0)
636+
return 0;
637+
638+
// set the number of application context entries
639+
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
640+
(void*) &numEntries, sizeof(ub4),
641+
OCI_ATTR_APPCTX_SIZE, self->environment->errorHandle);
642+
if (Environment_CheckForError(self->environment, status,
643+
"Connection_ProcessAppContext(): set app context size") < 0)
644+
return -1;
645+
646+
// get the application context list handle
647+
status = OCIAttrGet(self->sessionHandle, OCI_HTYPE_SESSION, &listHandle, 0,
648+
OCI_ATTR_APPCTX_LIST, self->environment->errorHandle);
649+
if (Environment_CheckForError(self->environment, status,
650+
"Connection_ProcessAppContext(): get list handle") < 0)
651+
return -1;
652+
653+
// set each application context entry
654+
for (i = 0; i < numEntries; i++) {
655+
656+
// get entry
657+
entryObj = PyList_GET_ITEM(appContextObj, i);
658+
if (!PyTuple_Check(entryObj) || PyTuple_GET_SIZE(entryObj) != 3) {
659+
PyErr_SetString(PyExc_TypeError,
660+
"appcontext should be a list of 3-tuples");
661+
return -1;
662+
}
663+
664+
// retrieve the context element descriptor
665+
status = OCIParamGet(listHandle, OCI_DTYPE_PARAM,
666+
self->environment->errorHandle, &entryHandle, i + 1);
667+
if (Environment_CheckForError(self->environment, status,
668+
"Connection_ProcessAppContext(): get entry handle") < 0)
669+
return -1;
670+
671+
// set the namespace name
672+
if (cxBuffer_FromObject(&buffer, PyTuple_GET_ITEM(entryObj, 0),
673+
self->environment->encoding) < 0)
674+
return -1;
675+
status = OCIAttrSet(entryHandle, OCI_DTYPE_PARAM,
676+
(void*) buffer.ptr, buffer.size, OCI_ATTR_APPCTX_NAME,
677+
self->environment->errorHandle);
678+
cxBuffer_Clear(&buffer);
679+
if (Environment_CheckForError(self->environment, status,
680+
"Connection_ProcessAppContext(): set namespace name") < 0)
681+
return -1;
682+
683+
// set the name
684+
if (cxBuffer_FromObject(&buffer, PyTuple_GET_ITEM(entryObj, 1),
685+
self->environment->encoding) < 0)
686+
return -1;
687+
status = OCIAttrSet(entryHandle, OCI_DTYPE_PARAM,
688+
(void*) buffer.ptr, buffer.size, OCI_ATTR_APPCTX_ATTR,
689+
self->environment->errorHandle);
690+
cxBuffer_Clear(&buffer);
691+
if (Environment_CheckForError(self->environment, status,
692+
"Connection_ProcessAppContext(): set name") < 0)
693+
return -1;
694+
695+
// set the value
696+
if (cxBuffer_FromObject(&buffer, PyTuple_GET_ITEM(entryObj, 2),
697+
self->environment->encoding) < 0)
698+
return -1;
699+
status = OCIAttrSet(entryHandle, OCI_DTYPE_PARAM,
700+
(void*) buffer.ptr, buffer.size, OCI_ATTR_APPCTX_VALUE,
701+
self->environment->errorHandle);
702+
cxBuffer_Clear(&buffer);
703+
if (Environment_CheckForError(self->environment, status,
704+
"Connection_ProcessAppContext(): set value") < 0)
705+
return -1;
706+
707+
}
708+
709+
return 0;
710+
}
711+
712+
610713
//-----------------------------------------------------------------------------
611714
// Connection_Connect()
612715
// Create a new connection object by connecting to the database.
@@ -620,7 +723,8 @@ static int Connection_Connect(
620723
PyObject *moduleObj, // session "module" value
621724
PyObject *actionObj, // session "action" value
622725
PyObject *clientinfoObj, // session "clientinfo" value
623-
PyObject *editionObj) // session "edition" value
726+
PyObject *editionObj, // session "edition" value
727+
PyObject *appContextObj) // app context value
624728
{
625729
ub4 credentialType = OCI_CRED_EXT;
626730
udt_Buffer buffer;
@@ -735,6 +839,7 @@ static int Connection_Connect(
735839
"Connection_Connect(): set session handle") < 0)
736840
return -1;
737841

842+
// set module (deprecated)
738843
if (moduleObj) {
739844
if (cxBuffer_FromObject(&buffer, moduleObj,
740845
self->environment->encoding))
@@ -748,6 +853,7 @@ static int Connection_Connect(
748853
return -1;
749854
}
750855

856+
// set action (deprecated)
751857
if (actionObj) {
752858
if (cxBuffer_FromObject(&buffer, actionObj,
753859
self->environment->encoding))
@@ -761,6 +867,7 @@ static int Connection_Connect(
761867
return -1;
762868
}
763869

870+
// set client info (deprecated)
764871
if (clientinfoObj) {
765872
if (cxBuffer_FromObject(&buffer, clientinfoObj,
766873
self->environment->encoding))
@@ -774,6 +881,7 @@ static int Connection_Connect(
774881
return -1;
775882
}
776883

884+
// set edition
777885
if (editionObj) {
778886
if (cxBuffer_FromObject(&buffer, editionObj,
779887
self->environment->encoding))
@@ -787,6 +895,10 @@ static int Connection_Connect(
787895
return -1;
788896
}
789897

898+
// set application context, if applicable
899+
if (appContextObj && Connection_ProcessAppContext(self, appContextObj) < 0)
900+
return -1;
901+
790902
// if a new password has been specified, change it which will also
791903
// establish the session
792904
if (newPasswordObj)
@@ -883,7 +995,7 @@ static int Connection_Init(
883995
{
884996
PyObject *usernameObj, *passwordObj, *dsnObj, *cclassObj, *editionObj;
885997
PyObject *threadedObj, *twophaseObj, *eventsObj, *newPasswordObj;
886-
PyObject *moduleObj, *actionObj, *clientinfoObj;
998+
PyObject *moduleObj, *actionObj, *clientinfoObj, *appContextObj;
887999
int threaded, twophase, events;
8881000
char *encoding, *nencoding;
8891001
ub4 connectMode, purity;
@@ -894,24 +1006,24 @@ static int Connection_Init(
8941006
static char *keywordList[] = { "user", "password", "dsn", "mode",
8951007
"handle", "pool", "threaded", "twophase", "events", "cclass",
8961008
"purity", "newpassword", "encoding", "nencoding", "module",
897-
"action", "clientinfo", "edition", NULL };
1009+
"action", "clientinfo", "edition", "appcontext", NULL };
8981010

8991011
// parse arguments
9001012
pool = NULL;
9011013
handle = NULL;
9021014
connectMode = OCI_STMT_CACHE;
9031015
threadedObj = twophaseObj = eventsObj = newPasswordObj = NULL;
9041016
usernameObj = passwordObj = dsnObj = cclassObj = editionObj = NULL;
905-
moduleObj = actionObj = clientinfoObj = NULL;
1017+
moduleObj = actionObj = clientinfoObj = appContextObj = NULL;
9061018
threaded = twophase = events = purity = 0;
9071019
encoding = nencoding = NULL;
9081020
purity = OCI_ATTR_PURITY_DEFAULT;
9091021
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs,
910-
"|OOOiiO!OOOOiOssOOOO", keywordList, &usernameObj, &passwordObj,
1022+
"|OOOiiO!OOOOiOssOOOOO", keywordList, &usernameObj, &passwordObj,
9111023
&dsnObj, &connectMode, &handle, &g_SessionPoolType, &pool,
9121024
&threadedObj, &twophaseObj, &eventsObj, &cclassObj, &purity,
9131025
&newPasswordObj, &encoding, &nencoding, &moduleObj, &actionObj,
914-
&clientinfoObj, &editionObj))
1026+
&clientinfoObj, &editionObj, &appContextObj))
9151027
return -1;
9161028
if (threadedObj) {
9171029
threaded = PyObject_IsTrue(threadedObj);
@@ -957,7 +1069,7 @@ static int Connection_Init(
9571069
purity);
9581070
return Connection_Connect(self, connectMode, twophase, passwordObj,
9591071
newPasswordObj, moduleObj, actionObj, clientinfoObj,
960-
editionObj);
1072+
editionObj, appContextObj);
9611073
}
9621074

9631075

0 commit comments

Comments
 (0)