SessionPool.c 20 KB

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