SessionPool.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. //-----------------------------------------------------------------------------
  2. // SessionPool.c
  3. // Handles session pooling (only available in Oracle 9i and up).
  4. //-----------------------------------------------------------------------------
  5. //-----------------------------------------------------------------------------
  6. // structure for the Python type "SessionPool"
  7. //-----------------------------------------------------------------------------
  8. typedef struct {
  9. PyObject_HEAD
  10. OCISPool *handle;
  11. ub4 minSessions;
  12. ub4 maxSessions;
  13. ub4 sessionIncrement;
  14. ub4 cacheSize;
  15. int homogeneous;
  16. PyObject *name;
  17. PyObject *username;
  18. PyObject *password;
  19. PyObject *dsn;
  20. udt_Environment *environment;
  21. PyTypeObject *connectionType;
  22. } udt_SessionPool;
  23. //-----------------------------------------------------------------------------
  24. // constants for the OCI attributes
  25. //-----------------------------------------------------------------------------
  26. static ub4 gc_OpenAttribute = OCI_ATTR_SPOOL_OPEN_COUNT;
  27. static ub4 gc_BusyAttribute = OCI_ATTR_SPOOL_BUSY_COUNT;
  28. static ub4 gc_TimeoutAttribute = OCI_ATTR_SPOOL_TIMEOUT;
  29. static ub4 gc_GetModeAttribute = OCI_ATTR_SPOOL_GETMODE;
  30. //-----------------------------------------------------------------------------
  31. // functions for the Python type "SessionPool"
  32. //-----------------------------------------------------------------------------
  33. static PyObject *SessionPool_New(PyTypeObject*, PyObject*, PyObject*);
  34. static int SessionPool_Init(udt_SessionPool*, PyObject*, PyObject*);
  35. static void SessionPool_Free(udt_SessionPool*);
  36. static PyObject *SessionPool_Acquire(udt_SessionPool*, PyObject*, PyObject*);
  37. static PyObject *SessionPool_Drop(udt_SessionPool*, PyObject*);
  38. static PyObject *SessionPool_Release(udt_SessionPool*, PyObject*);
  39. static PyObject *SessionPool_GetOCIAttr(udt_SessionPool*, ub4*);
  40. static int SessionPool_SetOCIAttr(udt_SessionPool*, PyObject*, ub4*);
  41. //-----------------------------------------------------------------------------
  42. // declaration of methods for Python type "SessionPool"
  43. //-----------------------------------------------------------------------------
  44. static PyMethodDef g_SessionPoolMethods[] = {
  45. { "acquire", (PyCFunction) SessionPool_Acquire,
  46. METH_VARARGS | METH_KEYWORDS },
  47. { "drop", (PyCFunction) SessionPool_Drop, METH_VARARGS },
  48. { "release", (PyCFunction) SessionPool_Release, METH_VARARGS },
  49. { NULL }
  50. };
  51. //-----------------------------------------------------------------------------
  52. // declaration of members for Python type "SessionPool"
  53. //-----------------------------------------------------------------------------
  54. static PyMemberDef g_SessionPoolMembers[] = {
  55. { "username", T_OBJECT, offsetof(udt_SessionPool, username), READONLY },
  56. { "password", T_OBJECT, offsetof(udt_SessionPool, password), 0 },
  57. { "dsn", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY },
  58. { "tnsentry", T_OBJECT, offsetof(udt_SessionPool, dsn), READONLY },
  59. { "name", T_OBJECT, offsetof(udt_SessionPool, name), READONLY },
  60. { "max", T_INT, offsetof(udt_SessionPool, maxSessions), READONLY },
  61. { "min", T_INT, offsetof(udt_SessionPool, minSessions), READONLY },
  62. { "increment", T_INT, offsetof(udt_SessionPool, sessionIncrement),
  63. READONLY },
  64. { "homogeneous", T_INT, offsetof(udt_SessionPool, homogeneous), READONLY },
  65. { NULL }
  66. };
  67. //-----------------------------------------------------------------------------
  68. // declaration of calculated members for Python type "SessionPool"
  69. //-----------------------------------------------------------------------------
  70. static PyGetSetDef g_SessionPoolCalcMembers[] = {
  71. { "opened", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_OpenAttribute },
  72. { "busy", (getter) SessionPool_GetOCIAttr, 0, 0, &gc_BusyAttribute },
  73. { "timeout", (getter) SessionPool_GetOCIAttr,
  74. (setter) SessionPool_SetOCIAttr, 0, &gc_TimeoutAttribute },
  75. { "getmode", (getter) SessionPool_GetOCIAttr,
  76. (setter) SessionPool_SetOCIAttr, 0, &gc_GetModeAttribute },
  77. { NULL }
  78. };
  79. //-----------------------------------------------------------------------------
  80. // declaration of Python type "SessionPool"
  81. //-----------------------------------------------------------------------------
  82. static PyTypeObject g_SessionPoolType = {
  83. PyVarObject_HEAD_INIT(NULL, 0)
  84. "OracleSessionPool", // tp_name
  85. sizeof(udt_SessionPool), // tp_basicsize
  86. 0, // tp_itemsize
  87. (destructor) SessionPool_Free, // tp_dealloc
  88. 0, // tp_print
  89. 0, // tp_getattr
  90. 0, // tp_setattr
  91. 0, // tp_compare
  92. 0, // tp_repr
  93. 0, // tp_as_number
  94. 0, // tp_as_sequence
  95. 0, // tp_as_mapping
  96. 0, // tp_hash
  97. 0, // tp_call
  98. 0, // tp_str
  99. 0, // tp_getattro
  100. 0, // tp_setattro
  101. 0, // tp_as_buffer
  102. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
  103. // tp_flags
  104. 0, // tp_doc
  105. 0, // tp_traverse
  106. 0, // tp_clear
  107. 0, // tp_richcompare
  108. 0, // tp_weaklistoffset
  109. 0, // tp_iter
  110. 0, // tp_iternext
  111. g_SessionPoolMethods, // tp_methods
  112. g_SessionPoolMembers, // tp_members
  113. g_SessionPoolCalcMembers, // tp_getset
  114. 0, // tp_base
  115. 0, // tp_dict
  116. 0, // tp_descr_get
  117. 0, // tp_descr_set
  118. 0, // tp_dictoffset
  119. (initproc) SessionPool_Init, // tp_init
  120. 0, // tp_alloc
  121. (newfunc) SessionPool_New, // tp_new
  122. 0, // tp_free
  123. 0, // tp_is_gc
  124. 0 // tp_bases
  125. };
  126. #include "Connection.c"
  127. //-----------------------------------------------------------------------------
  128. // SessionPool_New()
  129. // Create a new session pool object.
  130. //-----------------------------------------------------------------------------
  131. static PyObject *SessionPool_New(
  132. PyTypeObject *type, // type object
  133. PyObject *args, // arguments
  134. PyObject *keywordArgs) // keyword arguments
  135. {
  136. udt_SessionPool *newObject;
  137. // create the object
  138. newObject = (udt_SessionPool*) type->tp_alloc(type, 0);
  139. if (!newObject)
  140. return NULL;
  141. newObject->environment = NULL;
  142. return (PyObject*) newObject;
  143. }
  144. //-----------------------------------------------------------------------------
  145. // SessionPool_Init()
  146. // Initialize the session pool object.
  147. //-----------------------------------------------------------------------------
  148. static int SessionPool_Init(
  149. udt_SessionPool *self, // session pool object
  150. PyObject *args, // arguments
  151. PyObject *keywordArgs) // keyword arguments
  152. {
  153. unsigned minSessions, maxSessions, sessionIncrement;
  154. PyObject *threadedObj, *eventsObj, *homogeneousObj;
  155. udt_Buffer username, password, dsn;
  156. int threaded, events, homogeneous;
  157. PyTypeObject *connectionType;
  158. unsigned poolNameLength;
  159. const char *poolName;
  160. sword status;
  161. ub4 poolMode;
  162. ub1 getMode;
  163. // define keyword arguments
  164. static char *keywordList[] = { "user", "password", "dsn", "min", "max",
  165. "increment", "connectiontype", "threaded", "getmode", "events",
  166. "homogeneous", NULL };
  167. // parse arguments and keywords
  168. homogeneous = 1;
  169. threaded = events = 0;
  170. threadedObj = eventsObj = homogeneousObj = NULL;
  171. connectionType = &g_ConnectionType;
  172. getMode = OCI_SPOOL_ATTRVAL_NOWAIT;
  173. if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!O!O!iii|OObOO",
  174. keywordList, cxString_Type, &self->username,
  175. cxString_Type, &self->password, cxString_Type, &self->dsn,
  176. &minSessions, &maxSessions, &sessionIncrement, &connectionType,
  177. &threadedObj, &getMode, &eventsObj, &homogeneousObj))
  178. return -1;
  179. if (!PyType_Check(connectionType)) {
  180. PyErr_SetString(g_ProgrammingErrorException,
  181. "connectiontype must be a type");
  182. return -1;
  183. }
  184. if (!PyType_IsSubtype(connectionType, &g_ConnectionType)) {
  185. PyErr_SetString(g_ProgrammingErrorException,
  186. "connectiontype must be a subclass of Connection");
  187. return -1;
  188. }
  189. if (threadedObj) {
  190. threaded = PyObject_IsTrue(threadedObj);
  191. if (threaded < 0)
  192. return -1;
  193. }
  194. if (eventsObj) {
  195. events = PyObject_IsTrue(eventsObj);
  196. if (events < 0)
  197. return -1;
  198. }
  199. if (homogeneousObj) {
  200. homogeneous = PyObject_IsTrue(homogeneousObj);
  201. if (homogeneous < 0)
  202. return -1;
  203. }
  204. // initialize the object's members
  205. Py_INCREF(connectionType);
  206. self->connectionType = connectionType;
  207. Py_INCREF(self->dsn);
  208. Py_INCREF(self->username);
  209. Py_INCREF(self->password);
  210. self->minSessions = minSessions;
  211. self->maxSessions = maxSessions;
  212. self->sessionIncrement = sessionIncrement;
  213. self->homogeneous = homogeneous;
  214. // set up the environment
  215. self->environment = Environment_NewFromScratch(threaded, events, NULL,
  216. NULL);
  217. if (!self->environment)
  218. return -1;
  219. // create the session pool handle
  220. status = OCIHandleAlloc(self->environment->handle, (dvoid**) &self->handle,
  221. OCI_HTYPE_SPOOL, 0, 0);
  222. if (Environment_CheckForError(self->environment, status,
  223. "SessionPool_New(): allocate handle") < 0)
  224. return -1;
  225. // prepare pool mode
  226. poolMode = OCI_SPC_STMTCACHE;
  227. if (self->homogeneous)
  228. poolMode |= OCI_SPC_HOMOGENEOUS;
  229. // create the session pool
  230. if (cxBuffer_FromObject(&username, self->username,
  231. self->environment->encoding) < 0)
  232. return -1;
  233. if (cxBuffer_FromObject(&password, self->password,
  234. self->environment->encoding) < 0) {
  235. cxBuffer_Clear(&username);
  236. return -1;
  237. }
  238. if (cxBuffer_FromObject(&dsn, self->dsn,
  239. self->environment->encoding) < 0) {
  240. cxBuffer_Clear(&username);
  241. cxBuffer_Clear(&password);
  242. return -1;
  243. }
  244. Py_BEGIN_ALLOW_THREADS
  245. status = OCISessionPoolCreate(self->environment->handle,
  246. self->environment->errorHandle, self->handle,
  247. (OraText**) &poolName, &poolNameLength, (OraText*) dsn.ptr,
  248. dsn.size, minSessions, maxSessions, sessionIncrement,
  249. (OraText*) username.ptr, username.size, (OraText*) password.ptr,
  250. password.size, poolMode);
  251. Py_END_ALLOW_THREADS
  252. cxBuffer_Clear(&username);
  253. cxBuffer_Clear(&password);
  254. cxBuffer_Clear(&dsn);
  255. if (Environment_CheckForError(self->environment, status,
  256. "SessionPool_New(): create pool") < 0)
  257. return -1;
  258. // create the string for the pool name
  259. self->name = cxString_FromEncodedString(poolName, poolNameLength,
  260. self->environment->encoding);
  261. if (!self->name)
  262. return -1;
  263. // set the mode on the pool
  264. status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, (dvoid*) &getMode, 0,
  265. OCI_ATTR_SPOOL_GETMODE, self->environment->errorHandle);
  266. if (Environment_CheckForError(self->environment, status,
  267. "SessionPool_New(): set wait mode") < 0)
  268. return -1;
  269. return 0;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // SessionPool_Free()
  273. // Deallocate the session pool.
  274. //-----------------------------------------------------------------------------
  275. static void SessionPool_Free(
  276. udt_SessionPool *self) // session pool
  277. {
  278. if (self->handle) {
  279. OCISessionPoolDestroy(self->handle, self->environment->errorHandle,
  280. OCI_SPD_FORCE);
  281. OCIHandleFree(self->handle, OCI_HTYPE_SPOOL);
  282. }
  283. Py_XDECREF(self->name);
  284. Py_XDECREF(self->environment);
  285. Py_XDECREF(self->username);
  286. Py_XDECREF(self->password);
  287. Py_XDECREF(self->dsn);
  288. Py_TYPE(self)->tp_free((PyObject*) self);
  289. }
  290. //-----------------------------------------------------------------------------
  291. // SessionPool_IsConnected()
  292. // Determines if the session pool object is connected to the database. If
  293. // not, a Python exception is raised.
  294. //-----------------------------------------------------------------------------
  295. static int SessionPool_IsConnected(
  296. udt_SessionPool *self) // session pool
  297. {
  298. if (!self->handle) {
  299. PyErr_SetString(g_InterfaceErrorException, "not connected");
  300. return -1;
  301. }
  302. return 0;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // SessionPool_Acquire()
  306. // Create a new connection within the session pool.
  307. //-----------------------------------------------------------------------------
  308. static PyObject *SessionPool_Acquire(
  309. udt_SessionPool *self, // session pool
  310. PyObject *args, // arguments
  311. PyObject *keywordArgs) // keyword arguments
  312. {
  313. static char *keywordList[] = { "user", "password", "cclass", "purity",
  314. NULL };
  315. PyObject *createKeywordArgs, *result, *cclassObj, *purityObj;
  316. unsigned usernameLength, passwordLength;
  317. char *username, *password;
  318. // parse arguments
  319. username = NULL;
  320. password = NULL;
  321. if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|s#s#OO", keywordList,
  322. &username, &usernameLength, &password, &passwordLength, &cclassObj,
  323. &purityObj))
  324. return NULL;
  325. if (self->homogeneous && (username || password)) {
  326. PyErr_SetString(g_ProgrammingErrorException,
  327. "pool is homogeneous. Proxy authentication is not possible.");
  328. return NULL;
  329. }
  330. // make sure session pool is connected
  331. if (SessionPool_IsConnected(self) < 0)
  332. return NULL;
  333. // create arguments
  334. if (keywordArgs)
  335. createKeywordArgs = PyDict_Copy(keywordArgs);
  336. else createKeywordArgs = PyDict_New();
  337. if (!createKeywordArgs)
  338. return NULL;
  339. if (PyDict_SetItemString(createKeywordArgs, "pool",
  340. (PyObject*) self) < 0) {
  341. Py_DECREF(createKeywordArgs);
  342. return NULL;
  343. }
  344. // create the connection object
  345. result = PyObject_Call( (PyObject*) self->connectionType, args,
  346. createKeywordArgs);
  347. Py_DECREF(createKeywordArgs);
  348. return result;
  349. }
  350. //-----------------------------------------------------------------------------
  351. // SessionPool_InternalRelease()
  352. // Internal method used to release a connection back to the pool in order to
  353. // allow for the possibility of dropping the connection.
  354. //-----------------------------------------------------------------------------
  355. static PyObject *SessionPool_InternalRelease(
  356. udt_SessionPool *self, // session pool
  357. PyObject *args, // arguments
  358. ub4 mode) // OCI mode to use
  359. {
  360. udt_Connection *connection;
  361. sword status;
  362. // connection is expected
  363. if (!PyArg_ParseTuple(args, "O!", &g_ConnectionType, &connection))
  364. return NULL;
  365. // make sure session pool is connected
  366. if (SessionPool_IsConnected(self) < 0)
  367. return NULL;
  368. if (connection->sessionPool != self) {
  369. PyErr_SetString(g_ProgrammingErrorException,
  370. "connection not acquired with this session pool");
  371. return NULL;
  372. }
  373. // attempt a rollback but if dropping the connection from the pool
  374. // ignore the error
  375. Py_BEGIN_ALLOW_THREADS
  376. status = OCITransRollback(connection->handle,
  377. connection->environment->errorHandle, OCI_DEFAULT);
  378. Py_END_ALLOW_THREADS
  379. if (Environment_CheckForError(connection->environment, status,
  380. "SessionPool_Release(): rollback") < 0) {
  381. if (mode != OCI_SESSRLS_DROPSESS)
  382. return NULL;
  383. PyErr_Clear();
  384. }
  385. // release the connection
  386. Py_BEGIN_ALLOW_THREADS
  387. status = OCISessionRelease(connection->handle,
  388. connection->environment->errorHandle, NULL, 0, mode);
  389. Py_END_ALLOW_THREADS
  390. if (Environment_CheckForError(connection->environment, status,
  391. "SessionPool_Release(): release session") < 0)
  392. return NULL;
  393. // ensure that the connection behaves as closed
  394. Py_DECREF(connection->sessionPool);
  395. connection->sessionPool = NULL;
  396. connection->handle = NULL;
  397. Py_INCREF(Py_None);
  398. return Py_None;
  399. }
  400. //-----------------------------------------------------------------------------
  401. // SessionPool_Drop()
  402. // Release a connection back to the session pool, dropping it so that a new
  403. // connection will be created if needed.
  404. //-----------------------------------------------------------------------------
  405. static PyObject *SessionPool_Drop(
  406. udt_SessionPool *self, // session pool
  407. PyObject *args) // arguments
  408. {
  409. return SessionPool_InternalRelease(self, args, OCI_SESSRLS_DROPSESS);
  410. }
  411. //-----------------------------------------------------------------------------
  412. // SessionPool_Release()
  413. // Release a connection back to the session pool.
  414. //-----------------------------------------------------------------------------
  415. static PyObject *SessionPool_Release(
  416. udt_SessionPool *self, // session pool
  417. PyObject *args) // arguments
  418. {
  419. return SessionPool_InternalRelease(self, args, OCI_DEFAULT);
  420. }
  421. //-----------------------------------------------------------------------------
  422. // SessionPool_GetOCIAttr()
  423. // Return the value for the OCI attribute.
  424. //-----------------------------------------------------------------------------
  425. static PyObject *SessionPool_GetOCIAttr(
  426. udt_SessionPool *self, // session pool
  427. ub4 *attribute) // OCI attribute type
  428. {
  429. sword status;
  430. ub4 value;
  431. // make sure session pool is connected
  432. if (SessionPool_IsConnected(self) < 0)
  433. return NULL;
  434. // get the value from the OCI
  435. status = OCIAttrGet(self->handle, OCI_HTYPE_SPOOL, &value, 0, *attribute,
  436. self->environment->errorHandle);
  437. if (Environment_CheckForError(self->environment, status,
  438. "SessionPool_GetOCIAttr()") < 0)
  439. return NULL;
  440. if (*attribute == OCI_ATTR_SPOOL_GETMODE)
  441. return PyInt_FromLong((ub1) value);
  442. return PyInt_FromLong(value);
  443. }
  444. //-----------------------------------------------------------------------------
  445. // SessionPool_SetOCIAttr()
  446. // Set the value of the OCI attribute.
  447. //-----------------------------------------------------------------------------
  448. static int SessionPool_SetOCIAttr(
  449. udt_SessionPool *self, // session pool
  450. PyObject *value, // value to set
  451. ub4 *attribute) // OCI attribute type
  452. {
  453. ub4 ociValue;
  454. sword status;
  455. // make sure session pool is connected
  456. if (SessionPool_IsConnected(self) < 0)
  457. return -1;
  458. // set the value in the OCI
  459. if (!PyInt_Check(value)) {
  460. PyErr_SetString(PyExc_TypeError, "value must be an integer");
  461. return -1;
  462. }
  463. ociValue = PyInt_AsLong(value);
  464. if (PyErr_Occurred())
  465. return -1;
  466. status = OCIAttrSet(self->handle, OCI_HTYPE_SPOOL, &ociValue, 0,
  467. *attribute, self->environment->errorHandle);
  468. if (Environment_CheckForError(self->environment, status,
  469. "SessionPool_SetOCIAttr()") < 0)
  470. return -1;
  471. return 0;
  472. }