oci8.c 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Stig Sæther Bakken <ssb@php.net> |
  14. | Thies C. Arntzen <thies@thieso.net> |
  15. | Maxim Maletsky <maxim@maxim.cx> |
  16. | |
  17. | Collection support by Andy Sautins <asautins@veripost.net> |
  18. | Temporary LOB support by David Benson <dbenson@mancala.com> |
  19. | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at> |
  20. | |
  21. | Redesigned by: Antony Dovgal <antony@zend.com> |
  22. | Andi Gutmans <andi@php.net> |
  23. | Wez Furlong <wez@omniti.com> |
  24. +----------------------------------------------------------------------+
  25. */
  26. #ifdef HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29. #include "php.h"
  30. #include "ext/standard/info.h"
  31. #include "php_ini.h"
  32. #include "zend_smart_str.h"
  33. #ifdef HAVE_OCI8
  34. /* PHP 5.2 is the minimum supported version for OCI8 2.0 */
  35. #if PHP_MAJOR_VERSION < 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
  36. #error Use PHP OCI8 1.4 for your version of PHP
  37. #elif PHP_MAJOR_VERSION < 7
  38. /* PHP 7 is the minimum supported version for OCI8 2.1 */
  39. #error Use PHP OCI8 2.0 for your version of PHP
  40. #elif PHP_MAJOR_VERSION < 8
  41. /* PHP 8 is the minimum supported version for OCI8 3.0 */
  42. #error Use PHP OCI8 2.2 for your version of PHP
  43. #endif
  44. #include "php_oci8.h"
  45. #include "php_oci8_int.h"
  46. #include "zend_hash.h"
  47. ZEND_DECLARE_MODULE_GLOBALS(oci)
  48. static PHP_GINIT_FUNCTION(oci);
  49. static PHP_GSHUTDOWN_FUNCTION(oci);
  50. /* Allow PHP 5.3 branch to be used in PECL for 5.x compatible builds */
  51. #ifndef Z_ADDREF_P
  52. #define Z_ADDREF_P(x) ZVAL_ADDREF(x)
  53. #endif
  54. /* For a user friendly message about environment setup */
  55. #if defined(PHP_WIN32)
  56. #define PHP_OCI8_LIB_PATH_MSG "PATH"
  57. #elif defined(__APPLE__)
  58. #define PHP_OCI8_LIB_PATH_MSG "DYLD_LIBRARY_PATH"
  59. #elif defined(_AIX)
  60. #define PHP_OCI8_LIB_PATH_MSG "LIBPATH"
  61. #elif defined(__hpux)
  62. #define PHP_OCI8_LIB_PATH_MSG "SHLIB_PATH"
  63. #else
  64. #define PHP_OCI8_LIB_PATH_MSG "LD_LIBRARY_PATH"
  65. #endif
  66. /* True globals, no need for thread safety */
  67. int le_connection;
  68. int le_pconnection;
  69. int le_statement;
  70. int le_descriptor;
  71. int le_psessionpool;
  72. int le_collection;
  73. zend_class_entry *oci_lob_class_entry_ptr;
  74. zend_class_entry *oci_coll_class_entry_ptr;
  75. #ifndef SQLT_BFILEE
  76. #define SQLT_BFILEE 114
  77. #endif
  78. #ifndef SQLT_CFILEE
  79. #define SQLT_CFILEE 115
  80. #endif
  81. #ifdef ZTS
  82. #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT | OCI_THREADED | OCI_NO_MUTEX)
  83. #else
  84. #define PHP_OCI_INIT_MODE (OCI_DEFAULT | OCI_OBJECT)
  85. #endif
  86. /* {{{ static protos */
  87. static void php_oci_connection_list_dtor (zend_resource *);
  88. static void php_oci_pconnection_list_dtor (zend_resource *);
  89. static void php_oci_pconnection_list_np_dtor (zend_resource *);
  90. static void php_oci_statement_list_dtor (zend_resource *);
  91. static void php_oci_descriptor_list_dtor (zend_resource *);
  92. static void php_oci_spool_list_dtor(zend_resource *entry);
  93. static void php_oci_collection_list_dtor (zend_resource *);
  94. static int php_oci_persistent_helper(zval *zv);
  95. static int php_oci_connection_ping(php_oci_connection *);
  96. static int php_oci_connection_status(php_oci_connection *);
  97. static int php_oci_connection_close(php_oci_connection *);
  98. static void php_oci_spool_close(php_oci_spool *session_pool);
  99. static OCIEnv *php_oci_create_env(ub2 charsetid);
  100. static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode);
  101. static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode);
  102. static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid);
  103. static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, zend_string *hash_key, int charsetid);
  104. static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh);
  105. /* }}} */
  106. /* {{{ dynamically loadable module stuff */
  107. #if defined(COMPILE_DL_OCI8) || defined(COMPILE_DL_OCI8_11G) || defined(COMPILE_DL_OCI8_12C) || defined(COMPILE_DL_OCI8_19)
  108. ZEND_GET_MODULE(oci8)
  109. #endif /* COMPILE_DL */
  110. /* }}} */
  111. #include "oci8_arginfo.h"
  112. static PHP_INI_MH(OnUpdateOldCloseSemantics)
  113. {
  114. bool *p = (bool *) ZEND_INI_GET_ADDR();
  115. *p = zend_ini_parse_bool(new_value);
  116. if (*p) {
  117. zend_error(E_DEPRECATED, "Directive oci8.old_oci_close_semantics is deprecated");
  118. }
  119. return SUCCESS;
  120. }
  121. /* {{{ extension definition structures */
  122. zend_module_entry oci8_module_entry = {
  123. STANDARD_MODULE_HEADER,
  124. "oci8", /* extension name */
  125. ext_functions, /* extension function list */
  126. PHP_MINIT(oci), /* extension-wide startup function */
  127. PHP_MSHUTDOWN(oci), /* extension-wide shutdown function */
  128. PHP_RINIT(oci), /* per-request startup function */
  129. PHP_RSHUTDOWN(oci), /* per-request shutdown function */
  130. PHP_MINFO(oci), /* information function */
  131. PHP_OCI8_VERSION,
  132. PHP_MODULE_GLOBALS(oci), /* globals descriptor */
  133. PHP_GINIT(oci), /* globals ctor */
  134. PHP_GSHUTDOWN(oci), /* globals dtor */
  135. NULL, /* post deactivate */
  136. STANDARD_MODULE_PROPERTIES_EX
  137. };
  138. /* }}} */
  139. /* {{{ PHP_INI */
  140. PHP_INI_BEGIN()
  141. STD_PHP_INI_ENTRY( "oci8.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_oci_globals, oci_globals)
  142. STD_PHP_INI_ENTRY( "oci8.persistent_timeout", "-1", PHP_INI_SYSTEM, OnUpdateLong, persistent_timeout, zend_oci_globals, oci_globals)
  143. STD_PHP_INI_ENTRY( "oci8.ping_interval", "60", PHP_INI_SYSTEM, OnUpdateLong, ping_interval, zend_oci_globals, oci_globals)
  144. STD_PHP_INI_BOOLEAN("oci8.privileged_connect", "0", PHP_INI_SYSTEM, OnUpdateBool, privileged_connect, zend_oci_globals, oci_globals)
  145. STD_PHP_INI_ENTRY( "oci8.statement_cache_size", "20", PHP_INI_SYSTEM, OnUpdateLong, statement_cache_size, zend_oci_globals, oci_globals)
  146. STD_PHP_INI_ENTRY( "oci8.default_prefetch", "100", PHP_INI_SYSTEM, OnUpdateLong, default_prefetch, zend_oci_globals, oci_globals)
  147. STD_PHP_INI_BOOLEAN("oci8.old_oci_close_semantics", "0", PHP_INI_SYSTEM, OnUpdateOldCloseSemantics, old_oci_close_semantics,zend_oci_globals, oci_globals)
  148. #if (OCI_MAJOR_VERSION >= 11)
  149. STD_PHP_INI_ENTRY( "oci8.connection_class", "", PHP_INI_ALL, OnUpdateString, connection_class, zend_oci_globals, oci_globals)
  150. #endif
  151. #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
  152. STD_PHP_INI_BOOLEAN("oci8.events", "0", PHP_INI_SYSTEM, OnUpdateBool, events, zend_oci_globals, oci_globals)
  153. #endif
  154. PHP_INI_END()
  155. /* }}} */
  156. /* {{{ startup, shutdown and info functions */
  157. /* {{{ php_oci_init_global_handles()
  158. *
  159. * Initialize global handles only when they are needed
  160. */
  161. static void php_oci_init_global_handles(void)
  162. {
  163. sword errstatus;
  164. sb4 ora_error_code = 0;
  165. text tmp_buf[PHP_OCI_ERRBUF_LEN]; /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
  166. errstatus = OCIEnvNlsCreate(&OCI_G(env), PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, 0, 0);
  167. if (errstatus == OCI_ERROR) {
  168. #ifdef HAVE_OCI_INSTANT_CLIENT
  169. php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
  170. #else
  171. php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
  172. #endif
  173. if (OCI_G(env)
  174. && OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
  175. && *tmp_buf) {
  176. php_error_docref(NULL, E_WARNING, "%s", tmp_buf);
  177. }
  178. OCI_G(env) = NULL;
  179. OCI_G(err) = NULL;
  180. return;
  181. }
  182. errstatus = OCIHandleAlloc (OCI_G(env), (dvoid **)&OCI_G(err), OCI_HTYPE_ERROR, 0, NULL);
  183. if (errstatus == OCI_SUCCESS) {
  184. #if !defined(OCI_MAJOR_VERSION) || (OCI_MAJOR_VERSION < 11)
  185. /* This fixes PECL bug 15988 (sqlnet.ora not being read). The
  186. * root cause was fixed in Oracle 10.2.0.4 but there is no
  187. * compile time method to check for that precise patch level,
  188. * nor can it be guaranteed that runtime will use the same
  189. * patch level the code was compiled with. So, we do this
  190. * code for all non 11g versions.
  191. */
  192. OCICPool *cpoolh;
  193. ub4 cpoolmode = 0x80000000; /* Pass invalid mode to OCIConnectionPoolCreate */
  194. PHP_OCI_CALL(OCIHandleAlloc, (OCI_G(env), (dvoid **) &cpoolh, OCI_HTYPE_CPOOL, (size_t) 0, (dvoid **) 0));
  195. PHP_OCI_CALL(OCIConnectionPoolCreate, (OCI_G(env), OCI_G(err), cpoolh, NULL, 0, NULL, 0, 0, 0, 0, NULL, 0, NULL, 0, cpoolmode));
  196. PHP_OCI_CALL(OCIConnectionPoolDestroy, (cpoolh, OCI_G(err), OCI_DEFAULT));
  197. PHP_OCI_CALL(OCIHandleFree, (cpoolh, OCI_HTYPE_CPOOL));
  198. #endif
  199. } else {
  200. OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR);
  201. if (ora_error_code) {
  202. int tmp_buf_len = (int) strlen((char *)tmp_buf);
  203. if (tmp_buf_len > 0 && tmp_buf[tmp_buf_len - 1] == '\n') {
  204. tmp_buf[tmp_buf_len - 1] = '\0';
  205. }
  206. if (errstatus == OCI_SUCCESS_WITH_INFO) {
  207. php_error_docref(NULL, E_WARNING, "Initialization error: OCI_SUCCESS_WITH_INFO: %s", tmp_buf);
  208. } else {
  209. php_error_docref(NULL, E_WARNING, "Initialization error: OCI_ERROR: %s", tmp_buf);
  210. OCIHandleFree((dvoid *) OCI_G(env), OCI_HTYPE_ENV);
  211. OCI_G(env) = NULL;
  212. OCI_G(err) = NULL;
  213. }
  214. }
  215. }
  216. }
  217. /* }}} */
  218. /* {{{ PHP_GINIT_FUNCTION
  219. *
  220. * Zerofill globals during module init
  221. */
  222. static PHP_GINIT_FUNCTION(oci)
  223. {
  224. memset(oci_globals, 0, sizeof(zend_oci_globals));
  225. }
  226. /* }}} */
  227. /* {{{ PHP_GSHUTDOWN_FUNCTION
  228. *
  229. * Called for thread shutdown in ZTS, after module shutdown for non-ZTS
  230. * Free global handles (if they were initialized before)
  231. */
  232. static PHP_GSHUTDOWN_FUNCTION(oci)
  233. {
  234. if (oci_globals->err) {
  235. oci_globals->in_call = 1;
  236. OCIHandleFree((dvoid *) oci_globals->err, OCI_HTYPE_ERROR);
  237. oci_globals->in_call = 0;
  238. oci_globals->err = NULL;
  239. }
  240. if (oci_globals->env) {
  241. oci_globals->in_call = 1;
  242. OCIHandleFree((dvoid *) oci_globals->env, OCI_HTYPE_ENV);
  243. oci_globals->in_call = 0;
  244. oci_globals->env = NULL;
  245. }
  246. }
  247. /* }}} */
  248. PHP_MINIT_FUNCTION(oci)
  249. {
  250. REGISTER_INI_ENTRIES();
  251. le_statement = zend_register_list_destructors_ex(php_oci_statement_list_dtor, NULL, "oci8 statement", module_number);
  252. le_connection = zend_register_list_destructors_ex(php_oci_connection_list_dtor, NULL, "oci8 connection", module_number);
  253. le_pconnection = zend_register_list_destructors_ex(php_oci_pconnection_list_np_dtor, php_oci_pconnection_list_dtor, "oci8 persistent connection", module_number);
  254. le_psessionpool = zend_register_list_destructors_ex(NULL, php_oci_spool_list_dtor, "oci8 persistent session pool", module_number);
  255. le_descriptor = zend_register_list_destructors_ex(php_oci_descriptor_list_dtor, NULL, "oci8 descriptor", module_number);
  256. le_collection = zend_register_list_destructors_ex(php_oci_collection_list_dtor, NULL, "oci8 collection", module_number);
  257. oci_lob_class_entry_ptr = register_class_OCILob();
  258. oci_coll_class_entry_ptr = register_class_OCICollection();
  259. /* thies@thieso.net 990203 i do not think that we will need all of them - just in here for completeness for now! */
  260. REGISTER_LONG_CONSTANT("OCI_DEFAULT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
  261. REGISTER_LONG_CONSTANT("OCI_SYSOPER",OCI_SYSOPER, CONST_CS | CONST_PERSISTENT);
  262. REGISTER_LONG_CONSTANT("OCI_SYSDBA",OCI_SYSDBA, CONST_CS | CONST_PERSISTENT);
  263. REGISTER_LONG_CONSTANT("OCI_CRED_EXT",PHP_OCI_CRED_EXT, CONST_CS | CONST_PERSISTENT);
  264. REGISTER_LONG_CONSTANT("OCI_DESCRIBE_ONLY",OCI_DESCRIBE_ONLY, CONST_CS | CONST_PERSISTENT);
  265. REGISTER_LONG_CONSTANT("OCI_COMMIT_ON_SUCCESS",OCI_COMMIT_ON_SUCCESS, CONST_CS | CONST_PERSISTENT);
  266. REGISTER_LONG_CONSTANT("OCI_NO_AUTO_COMMIT",OCI_DEFAULT, CONST_CS | CONST_PERSISTENT);
  267. REGISTER_LONG_CONSTANT("OCI_EXACT_FETCH",OCI_EXACT_FETCH, CONST_CS | CONST_PERSISTENT);
  268. /* for $LOB->seek() */
  269. REGISTER_LONG_CONSTANT("OCI_SEEK_SET",PHP_OCI_SEEK_SET, CONST_CS | CONST_PERSISTENT);
  270. REGISTER_LONG_CONSTANT("OCI_SEEK_CUR",PHP_OCI_SEEK_CUR, CONST_CS | CONST_PERSISTENT);
  271. REGISTER_LONG_CONSTANT("OCI_SEEK_END",PHP_OCI_SEEK_END, CONST_CS | CONST_PERSISTENT);
  272. /* for $LOB->flush() */
  273. REGISTER_LONG_CONSTANT("OCI_LOB_BUFFER_FREE",OCI_LOB_BUFFER_FREE, CONST_CS | CONST_PERSISTENT);
  274. /* for OCIBindByName (real "oci" names + short "php" names */
  275. REGISTER_LONG_CONSTANT("SQLT_BFILEE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
  276. REGISTER_LONG_CONSTANT("SQLT_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
  277. REGISTER_LONG_CONSTANT("SQLT_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
  278. REGISTER_LONG_CONSTANT("SQLT_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
  279. REGISTER_LONG_CONSTANT("SQLT_RDD",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
  280. REGISTER_LONG_CONSTANT("SQLT_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
  281. REGISTER_LONG_CONSTANT("SQLT_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
  282. REGISTER_LONG_CONSTANT("SQLT_RSET",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
  283. REGISTER_LONG_CONSTANT("SQLT_AFC",SQLT_AFC, CONST_CS | CONST_PERSISTENT);
  284. REGISTER_LONG_CONSTANT("SQLT_CHR",SQLT_CHR, CONST_CS | CONST_PERSISTENT);
  285. REGISTER_LONG_CONSTANT("SQLT_VCS",SQLT_VCS, CONST_CS | CONST_PERSISTENT);
  286. REGISTER_LONG_CONSTANT("SQLT_AVC",SQLT_AVC, CONST_CS | CONST_PERSISTENT);
  287. REGISTER_LONG_CONSTANT("SQLT_STR",SQLT_STR, CONST_CS | CONST_PERSISTENT);
  288. REGISTER_LONG_CONSTANT("SQLT_LVC",SQLT_LVC, CONST_CS | CONST_PERSISTENT);
  289. REGISTER_LONG_CONSTANT("SQLT_FLT",SQLT_FLT, CONST_CS | CONST_PERSISTENT);
  290. REGISTER_LONG_CONSTANT("SQLT_UIN",SQLT_UIN, CONST_CS | CONST_PERSISTENT);
  291. REGISTER_LONG_CONSTANT("SQLT_LNG",SQLT_LNG, CONST_CS | CONST_PERSISTENT);
  292. REGISTER_LONG_CONSTANT("SQLT_LBI",SQLT_LBI, CONST_CS | CONST_PERSISTENT);
  293. REGISTER_LONG_CONSTANT("SQLT_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
  294. REGISTER_LONG_CONSTANT("SQLT_ODT",SQLT_ODT, CONST_CS | CONST_PERSISTENT);
  295. #if defined(HAVE_OCI_INSTANT_CLIENT) || (defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 10)
  296. REGISTER_LONG_CONSTANT("SQLT_BDOUBLE",SQLT_BDOUBLE, CONST_CS | CONST_PERSISTENT);
  297. REGISTER_LONG_CONSTANT("SQLT_BFLOAT",SQLT_BFLOAT, CONST_CS | CONST_PERSISTENT);
  298. #endif
  299. #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
  300. REGISTER_LONG_CONSTANT("SQLT_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
  301. #endif
  302. REGISTER_LONG_CONSTANT("OCI_B_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
  303. REGISTER_LONG_CONSTANT("SQLT_NTY",SQLT_NTY, CONST_CS | CONST_PERSISTENT);
  304. REGISTER_STRING_CONSTANT("OCI_SYSDATE","SYSDATE", CONST_CS | CONST_PERSISTENT);
  305. REGISTER_LONG_CONSTANT("OCI_B_BFILE",SQLT_BFILEE, CONST_CS | CONST_PERSISTENT);
  306. REGISTER_LONG_CONSTANT("OCI_B_CFILEE",SQLT_CFILEE, CONST_CS | CONST_PERSISTENT);
  307. REGISTER_LONG_CONSTANT("OCI_B_CLOB",SQLT_CLOB, CONST_CS | CONST_PERSISTENT);
  308. REGISTER_LONG_CONSTANT("OCI_B_BLOB",SQLT_BLOB, CONST_CS | CONST_PERSISTENT);
  309. REGISTER_LONG_CONSTANT("OCI_B_ROWID",SQLT_RDD, CONST_CS | CONST_PERSISTENT);
  310. REGISTER_LONG_CONSTANT("OCI_B_CURSOR",SQLT_RSET, CONST_CS | CONST_PERSISTENT);
  311. REGISTER_LONG_CONSTANT("OCI_B_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT);
  312. REGISTER_LONG_CONSTANT("OCI_B_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT);
  313. REGISTER_LONG_CONSTANT("OCI_B_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT);
  314. #if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12
  315. REGISTER_LONG_CONSTANT("OCI_B_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT);
  316. #endif
  317. /* for OCIFetchStatement */
  318. REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_COLUMN", PHP_OCI_FETCHSTATEMENT_BY_COLUMN, CONST_CS | CONST_PERSISTENT);
  319. REGISTER_LONG_CONSTANT("OCI_FETCHSTATEMENT_BY_ROW", PHP_OCI_FETCHSTATEMENT_BY_ROW, CONST_CS | CONST_PERSISTENT);
  320. /* for OCIFetchInto & OCIResult */
  321. REGISTER_LONG_CONSTANT("OCI_ASSOC",PHP_OCI_ASSOC, CONST_CS | CONST_PERSISTENT);
  322. REGISTER_LONG_CONSTANT("OCI_NUM",PHP_OCI_NUM, CONST_CS | CONST_PERSISTENT);
  323. REGISTER_LONG_CONSTANT("OCI_BOTH",PHP_OCI_BOTH, CONST_CS | CONST_PERSISTENT);
  324. REGISTER_LONG_CONSTANT("OCI_RETURN_NULLS",PHP_OCI_RETURN_NULLS, CONST_CS | CONST_PERSISTENT);
  325. REGISTER_LONG_CONSTANT("OCI_RETURN_LOBS",PHP_OCI_RETURN_LOBS, CONST_CS | CONST_PERSISTENT);
  326. /* for OCINewDescriptor (real "oci" names + short "php" names */
  327. REGISTER_LONG_CONSTANT("OCI_DTYPE_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
  328. REGISTER_LONG_CONSTANT("OCI_DTYPE_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
  329. REGISTER_LONG_CONSTANT("OCI_DTYPE_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
  330. REGISTER_LONG_CONSTANT("OCI_D_FILE",OCI_DTYPE_FILE, CONST_CS | CONST_PERSISTENT);
  331. REGISTER_LONG_CONSTANT("OCI_D_LOB",OCI_DTYPE_LOB, CONST_CS | CONST_PERSISTENT);
  332. REGISTER_LONG_CONSTANT("OCI_D_ROWID",OCI_DTYPE_ROWID, CONST_CS | CONST_PERSISTENT);
  333. /* for OCIWriteTemporaryLob */
  334. REGISTER_LONG_CONSTANT("OCI_TEMP_CLOB",OCI_TEMP_CLOB, CONST_CS | CONST_PERSISTENT);
  335. REGISTER_LONG_CONSTANT("OCI_TEMP_BLOB",OCI_TEMP_BLOB, CONST_CS | CONST_PERSISTENT);
  336. /* for Transparent Application Failover */
  337. REGISTER_LONG_CONSTANT("OCI_FO_END", OCI_FO_END, CONST_CS | CONST_PERSISTENT);
  338. REGISTER_LONG_CONSTANT("OCI_FO_ABORT", OCI_FO_ABORT, CONST_CS | CONST_PERSISTENT);
  339. REGISTER_LONG_CONSTANT("OCI_FO_REAUTH", OCI_FO_REAUTH, CONST_CS | CONST_PERSISTENT);
  340. REGISTER_LONG_CONSTANT("OCI_FO_BEGIN", OCI_FO_BEGIN, CONST_CS | CONST_PERSISTENT);
  341. REGISTER_LONG_CONSTANT("OCI_FO_ERROR", OCI_FO_ERROR, CONST_CS | CONST_PERSISTENT);
  342. REGISTER_LONG_CONSTANT("OCI_FO_NONE", OCI_FO_NONE, CONST_CS | CONST_PERSISTENT);
  343. REGISTER_LONG_CONSTANT("OCI_FO_SESSION", OCI_FO_SESSION, CONST_CS | CONST_PERSISTENT);
  344. REGISTER_LONG_CONSTANT("OCI_FO_SELECT", OCI_FO_SELECT, CONST_CS | CONST_PERSISTENT);
  345. REGISTER_LONG_CONSTANT("OCI_FO_TXNAL", OCI_FO_TXNAL, CONST_CS | CONST_PERSISTENT);
  346. REGISTER_LONG_CONSTANT("OCI_FO_RETRY", OCI_FO_RETRY, CONST_CS | CONST_PERSISTENT);
  347. return SUCCESS;
  348. }
  349. PHP_RINIT_FUNCTION(oci)
  350. {
  351. OCI_G(num_links) = OCI_G(num_persistent);
  352. OCI_G(errcode) = 0;
  353. OCI_G(edition) = NULL;
  354. return SUCCESS;
  355. }
  356. PHP_MSHUTDOWN_FUNCTION(oci)
  357. {
  358. OCI_G(shutdown) = 1;
  359. UNREGISTER_INI_ENTRIES();
  360. return SUCCESS;
  361. }
  362. PHP_RSHUTDOWN_FUNCTION(oci)
  363. {
  364. /* Check persistent connections and do the necessary actions if needed. If persistent_helper is
  365. * unable to process a pconnection because of a refcount, the processing would happen from
  366. * np-destructor which is called when refcount goes to zero - php_oci_pconnection_list_np_dtor
  367. */
  368. zend_hash_apply(&EG(persistent_list), php_oci_persistent_helper);
  369. if (OCI_G(edition)) {
  370. efree(OCI_G(edition));
  371. }
  372. return SUCCESS;
  373. }
  374. PHP_MINFO_FUNCTION(oci)
  375. {
  376. char buf[32];
  377. #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
  378. char ver[256];
  379. #endif
  380. php_info_print_table_start();
  381. php_info_print_table_row(2, "OCI8 Support", "enabled");
  382. #if defined(HAVE_OCI8_DTRACE)
  383. php_info_print_table_row(2, "OCI8 DTrace Support", "enabled");
  384. #else
  385. php_info_print_table_row(2, "OCI8 DTrace Support", "disabled");
  386. #endif
  387. php_info_print_table_row(2, "OCI8 Version", PHP_OCI8_VERSION);
  388. #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))
  389. php_oci_client_get_version(ver, sizeof(ver));
  390. php_info_print_table_row(2, "Oracle Run-time Client Library Version", ver);
  391. #else
  392. php_info_print_table_row(2, "Oracle Run-time Client Library Version", "Unknown");
  393. #endif
  394. #if defined(OCI_MAJOR_VERSION) && defined(OCI_MINOR_VERSION)
  395. snprintf(buf, sizeof(buf), "%d.%d", OCI_MAJOR_VERSION, OCI_MINOR_VERSION);
  396. #elif defined(PHP_OCI8_ORACLE_VERSION)
  397. snprintf(buf, sizeof(buf), "%s", PHP_OCI8_ORACLE_VERSION);
  398. #else
  399. snprintf(buf, sizeof(buf), "Unknown");
  400. #endif
  401. #if defined(HAVE_OCI_INSTANT_CLIENT)
  402. php_info_print_table_row(2, "Oracle Compile-time Instant Client Version", buf);
  403. #else
  404. php_info_print_table_row(2, "Oracle Compile-time Version", buf);
  405. #endif
  406. #if !defined(PHP_WIN32) && !defined(HAVE_OCI_INSTANT_CLIENT)
  407. #if defined(PHP_OCI8_DEF_DIR)
  408. php_info_print_table_row(2, "Compile-time ORACLE_HOME", PHP_OCI8_DEF_DIR);
  409. #endif
  410. #if defined(PHP_OCI8_DEF_SHARED_LIBADD)
  411. php_info_print_table_row(2, "Libraries Used", PHP_OCI8_DEF_SHARED_LIBADD);
  412. #endif
  413. #endif
  414. php_info_print_table_end();
  415. DISPLAY_INI_ENTRIES();
  416. php_info_print_table_start();
  417. php_info_print_table_header(2, "Statistics", "");
  418. snprintf(buf, sizeof(buf), ZEND_LONG_FMT, OCI_G(num_persistent));
  419. php_info_print_table_row(2, "Active Persistent Connections", buf);
  420. snprintf(buf, sizeof(buf), ZEND_LONG_FMT, OCI_G(num_links));
  421. php_info_print_table_row(2, "Active Connections", buf);
  422. php_info_print_table_end();
  423. }
  424. /* }}} */
  425. /* {{{ list destructors */
  426. /* {{{ php_oci_connection_list_dtor()
  427. *
  428. * Non-persistent connection destructor
  429. */
  430. static void php_oci_connection_list_dtor(zend_resource *entry)
  431. {
  432. php_oci_connection *connection = (php_oci_connection *)entry->ptr;
  433. if (connection) {
  434. php_oci_connection_close(connection);
  435. OCI_G(num_links)--;
  436. }
  437. }
  438. /* }}} */
  439. /* {{{ php_oci_pconnection_list_dtor()
  440. *
  441. * Persistent connection destructor
  442. */
  443. static void php_oci_pconnection_list_dtor(zend_resource *entry)
  444. {
  445. php_oci_connection *connection = (php_oci_connection *)entry->ptr;
  446. if (connection) {
  447. php_oci_connection_close(connection);
  448. OCI_G(num_persistent)--;
  449. OCI_G(num_links)--;
  450. }
  451. }
  452. /* }}} */
  453. /* {{{ php_oci_pconnection_list_np_dtor()
  454. *
  455. * Non-Persistent destructor for persistent connection - This gets invoked when
  456. * the refcount of this goes to zero in the regular list
  457. */
  458. static void php_oci_pconnection_list_np_dtor(zend_resource *entry)
  459. {
  460. php_oci_connection *connection = (php_oci_connection *)entry->ptr;
  461. zval *zvp;
  462. zend_resource *le;
  463. /*
  464. * We cannot get connection as NULL or as a stub in this function. This is the function that
  465. * turns a pconnection to a stub
  466. *
  467. * If oci_password_change() changed the password of a persistent connection, close the
  468. * connection and remove it from the persistent connection cache. This means subsequent scripts
  469. * will be prevented from being able to present the old (now invalid) password to a usable
  470. * connection to the database; they must use the new password.
  471. *
  472. * Check for conditions that warrant removal of the hash entry
  473. */
  474. if (!connection->is_open ||
  475. connection->passwd_changed ||
  476. (PG(connection_status) & PHP_CONNECTION_TIMEOUT) ||
  477. OCI_G(in_call)) {
  478. /* Remove the hash entry if present */
  479. if (connection->hash_key) {
  480. zvp = zend_hash_find(&EG(persistent_list), connection->hash_key);
  481. le = zvp ? Z_RES_P(zvp) : NULL;
  482. if (le != NULL && le->type == le_pconnection && le->ptr == connection) {
  483. zend_hash_del(&EG(persistent_list), connection->hash_key);
  484. }
  485. else {
  486. php_oci_connection_close(connection);
  487. OCI_G(num_persistent)--;
  488. }
  489. }
  490. #ifdef HAVE_OCI8_DTRACE
  491. if (DTRACE_OCI8_CONNECT_P_DTOR_CLOSE_ENABLED()) {
  492. DTRACE_OCI8_CONNECT_P_DTOR_CLOSE(connection);
  493. }
  494. #endif /* HAVE_OCI8_DTRACE */
  495. } else {
  496. /*
  497. * Release the connection to underlying pool. We do this unconditionally so that
  498. * out-of-scope pconnects are now consistent with oci_close and out-of-scope new connect
  499. * semantics. With the PECL OCI 1.3.x extensions, we release pconnections when oci_close
  500. * takes the refcount to zero.
  501. *
  502. * If oci_old_close_semantics is set, we artificially bump up the refcount and decremented
  503. * only at request shutdown.
  504. */
  505. php_oci_connection_release(connection);
  506. #ifdef HAVE_OCI8_DTRACE
  507. if (DTRACE_OCI8_CONNECT_P_DTOR_RELEASE_ENABLED()) {
  508. DTRACE_OCI8_CONNECT_P_DTOR_RELEASE(connection);
  509. }
  510. #endif /* HAVE_OCI8_DTRACE */
  511. }
  512. }
  513. /* }}} */
  514. /* {{{ php_oci_statement_list_dtor()
  515. *
  516. * Statement destructor
  517. */
  518. static void php_oci_statement_list_dtor(zend_resource *entry)
  519. {
  520. php_oci_statement *statement = (php_oci_statement *)entry->ptr;
  521. php_oci_statement_free(statement);
  522. }
  523. /* }}} */
  524. /* {{{ php_oci_descriptor_list_dtor()
  525. *
  526. * Descriptor destructor
  527. */
  528. static void php_oci_descriptor_list_dtor(zend_resource *entry)
  529. {
  530. php_oci_descriptor *descriptor = (php_oci_descriptor *)entry->ptr;
  531. php_oci_lob_free(descriptor);
  532. }
  533. /* }}} */
  534. /* {{{ php_oci_collection_list_dtor()
  535. *
  536. * Collection destructor
  537. */
  538. static void php_oci_collection_list_dtor(zend_resource *entry)
  539. {
  540. php_oci_collection *collection = (php_oci_collection *)entry->ptr;
  541. php_oci_collection_close(collection);
  542. }
  543. /* }}} */
  544. /* }}} */
  545. /* {{{ Hash Destructors */
  546. /* {{{ php_oci_define_hash_dtor()
  547. *
  548. * Define hash destructor
  549. */
  550. void php_oci_define_hash_dtor(zval *data)
  551. {
  552. php_oci_define *define = (php_oci_define *) Z_PTR_P(data);
  553. if (define->name) {
  554. efree(define->name);
  555. define->name = NULL;
  556. }
  557. zval_ptr_dtor(&define->val);
  558. efree(define);
  559. }
  560. /* }}} */
  561. /* {{{ php_oci_bind_hash_dtor()
  562. *
  563. * Bind hash destructor
  564. */
  565. void php_oci_bind_hash_dtor(zval *data)
  566. {
  567. php_oci_bind *bind = (php_oci_bind *) Z_PTR_P(data);
  568. if (!Z_ISUNDEF(bind->val)) {
  569. zval_ptr_dtor(&bind->val);
  570. ZVAL_UNDEF(&bind->val);
  571. }
  572. if (bind->array.elements) {
  573. efree(bind->array.elements);
  574. bind->array.elements = NULL;
  575. }
  576. if (bind->array.element_lengths) {
  577. efree(bind->array.element_lengths);
  578. bind->array.element_lengths = NULL;
  579. }
  580. if (bind->array.indicators) {
  581. efree(bind->array.indicators);
  582. bind->array.indicators = NULL;
  583. }
  584. efree(bind);
  585. }
  586. /* }}} */
  587. /* {{{ php_oci_column_hash_dtor()
  588. *
  589. * Column hash destructor
  590. */
  591. void php_oci_column_hash_dtor(zval *data)
  592. {
  593. php_oci_out_column *column = (php_oci_out_column *) Z_PTR_P(data);
  594. if (column->stmtid) {
  595. zend_list_close(column->stmtid);
  596. }
  597. if (column->descid) {
  598. if (GC_REFCOUNT(column->descid) == 1)
  599. zend_list_close(column->descid);
  600. else {
  601. GC_DELREF(column->descid);
  602. }
  603. }
  604. if (column->data) {
  605. efree(column->data);
  606. }
  607. if (column->name) {
  608. efree(column->name);
  609. }
  610. efree(column);
  611. }
  612. /* }}} */
  613. /* {{{ php_oci_descriptor_flush_hash_dtor()
  614. *
  615. * Flush descriptors on commit
  616. */
  617. void php_oci_descriptor_flush_hash_dtor(zval *data)
  618. {
  619. php_oci_descriptor *descriptor = (php_oci_descriptor *) Z_PTR_P(data);
  620. if (descriptor && descriptor->buffering == PHP_OCI_LOB_BUFFER_USED && (descriptor->type == OCI_DTYPE_LOB || descriptor->type == OCI_DTYPE_FILE)) {
  621. php_oci_lob_flush(descriptor, OCI_LOB_BUFFER_FREE);
  622. descriptor->buffering = PHP_OCI_LOB_BUFFER_ENABLED;
  623. }
  624. data = NULL;
  625. }
  626. /* }}} */
  627. /* }}} */
  628. /* {{{ php_oci_connection_descriptors_free()
  629. *
  630. * Free descriptors for a connection
  631. */
  632. void php_oci_connection_descriptors_free(php_oci_connection *connection)
  633. {
  634. zend_hash_destroy(connection->descriptors);
  635. efree(connection->descriptors);
  636. connection->descriptors = NULL;
  637. connection->descriptor_count = 0;
  638. }
  639. /* }}} */
  640. /* {{{ php_oci_error()
  641. *
  642. * Fetch & print out error message if we get an error
  643. * Returns an Oracle error number
  644. */
  645. sb4 php_oci_error(OCIError *err_p, sword errstatus)
  646. {
  647. text errbuf[PHP_OCI_ERRBUF_LEN];
  648. sb4 errcode = 0; /* Oracle error number */
  649. switch (errstatus) {
  650. case OCI_SUCCESS:
  651. break;
  652. case OCI_SUCCESS_WITH_INFO:
  653. errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
  654. if (errcode) {
  655. php_error_docref(NULL, E_WARNING, "OCI_SUCCESS_WITH_INFO: %s", errbuf);
  656. } else {
  657. php_error_docref(NULL, E_WARNING, "OCI_SUCCESS_WITH_INFO: failed to fetch error message");
  658. }
  659. break;
  660. case OCI_NEED_DATA:
  661. php_error_docref(NULL, E_WARNING, "OCI_NEED_DATA");
  662. break;
  663. case OCI_NO_DATA:
  664. errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
  665. if (errcode) {
  666. php_error_docref(NULL, E_WARNING, "%s", errbuf);
  667. } else {
  668. php_error_docref(NULL, E_WARNING, "OCI_NO_DATA: failed to fetch error message");
  669. }
  670. break;
  671. case OCI_ERROR:
  672. errcode = php_oci_fetch_errmsg(err_p, errbuf, sizeof(errbuf));
  673. if (errcode) {
  674. php_error_docref(NULL, E_WARNING, "%s", errbuf);
  675. } else {
  676. php_error_docref(NULL, E_WARNING, "Failed to fetch error message");
  677. }
  678. break;
  679. case OCI_INVALID_HANDLE:
  680. php_error_docref(NULL, E_WARNING, "OCI_INVALID_HANDLE");
  681. break;
  682. case OCI_STILL_EXECUTING:
  683. php_error_docref(NULL, E_WARNING, "OCI_STILL_EXECUTING");
  684. break;
  685. case OCI_CONTINUE:
  686. php_error_docref(NULL, E_WARNING, "OCI_CONTINUE");
  687. break;
  688. default:
  689. php_error_docref(NULL, E_WARNING, "Unknown OCI error code: %d", errstatus);
  690. break;
  691. }
  692. #ifdef HAVE_OCI8_DTRACE
  693. if (DTRACE_OCI8_ERROR_ENABLED()) {
  694. DTRACE_OCI8_ERROR((int)errstatus, (long)errcode);
  695. }
  696. #endif /* HAVE_OCI8_DTRACE */
  697. return errcode;
  698. }
  699. /* }}} */
  700. /* {{{ php_oci_fetch_errmsg()
  701. *
  702. * Fetch error message into the buffer from the error handle provided
  703. */
  704. sb4 php_oci_fetch_errmsg(OCIError *error_handle, text *error_buf, size_t error_buf_size)
  705. {
  706. sb4 error_code = 0;
  707. PHP_OCI_CALL(OCIErrorGet, (error_handle, (ub4)1, NULL, &error_code, error_buf, (ub4)error_buf_size, (ub4)OCI_HTYPE_ERROR));
  708. if (error_code) {
  709. int err_buf_len = (int) strlen((char *)error_buf);
  710. if (err_buf_len && error_buf[err_buf_len - 1] == '\n') {
  711. error_buf[err_buf_len - 1] = '\0';
  712. }
  713. }
  714. return error_code;
  715. }
  716. /* }}} */
  717. /* {{{ php_oci_fetch_sqltext_offset()
  718. *
  719. * Compute offset in the SQL statement
  720. */
  721. int php_oci_fetch_sqltext_offset(php_oci_statement *statement, text **sqltext, ub2 *error_offset)
  722. {
  723. sword errstatus;
  724. *sqltext = NULL;
  725. *error_offset = 0;
  726. PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (dvoid *) sqltext, (ub4 *)0, OCI_ATTR_STATEMENT, statement->err));
  727. if (errstatus != OCI_SUCCESS) {
  728. statement->errcode = php_oci_error(statement->err, errstatus);
  729. PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
  730. return 1;
  731. }
  732. PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)statement->stmt, OCI_HTYPE_STMT, (ub2 *)error_offset, (ub4 *)0, OCI_ATTR_PARSE_ERROR_OFFSET, statement->err));
  733. if (errstatus != OCI_SUCCESS) {
  734. statement->errcode = php_oci_error(statement->err, errstatus);
  735. PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
  736. return 1;
  737. }
  738. return 0;
  739. }
  740. /* }}} */
  741. /* {{{ php_oci_do_connect()
  742. *
  743. * Connect wrapper
  744. */
  745. void php_oci_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent, int exclusive)
  746. {
  747. php_oci_connection *connection;
  748. char *username, *password;
  749. char *dbname = NULL, *charset = NULL;
  750. size_t username_len = 0, password_len = 0;
  751. size_t dbname_len = 0, charset_len = 0;
  752. zend_long session_mode = OCI_DEFAULT;
  753. /* if a fourth parameter is handed over, it is the charset identifier (but is only used in Oracle 9i+) */
  754. ZEND_PARSE_PARAMETERS_START(2, 5)
  755. Z_PARAM_STRING(username, username_len)
  756. Z_PARAM_STRING(password, password_len)
  757. Z_PARAM_OPTIONAL
  758. Z_PARAM_STRING_OR_NULL(dbname, dbname_len)
  759. Z_PARAM_STRING(charset, charset_len)
  760. Z_PARAM_LONG(session_mode)
  761. ZEND_PARSE_PARAMETERS_END();
  762. #ifdef HAVE_OCI8_DTRACE
  763. if (DTRACE_OCI8_CONNECT_ENTRY_ENABLED()) {
  764. DTRACE_OCI8_CONNECT_ENTRY(username, dbname, charset, session_mode, persistent, exclusive);
  765. }
  766. #endif /* HAVE_OCI8_DTRACE */
  767. if (!charset_len) {
  768. charset = NULL;
  769. }
  770. connection = php_oci_do_connect_ex(username, (int) username_len, password, (int) password_len, NULL, 0, dbname, (int) dbname_len, charset, session_mode, persistent, exclusive);
  771. #ifdef HAVE_OCI8_DTRACE
  772. if (DTRACE_OCI8_CONNECT_RETURN_ENABLED()) {
  773. DTRACE_OCI8_CONNECT_RETURN(connection);
  774. }
  775. #endif /* HAVE_OCI8_DTRACE */
  776. if (!connection) {
  777. RETURN_FALSE;
  778. }
  779. RETURN_RES(connection->id);
  780. }
  781. /* }}} */
  782. /* {{{ php_oci_do_connect_ex()
  783. *
  784. * The real connect function. Allocates all the resources needed, establishes the connection and
  785. * returns the result handle (or NULL)
  786. */
  787. php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, char *dbname, int dbname_len, char *charset, zend_long session_mode, int persistent, int exclusive)
  788. {
  789. zval *zvp;
  790. zend_resource *le = NULL;
  791. zend_resource new_le;
  792. php_oci_connection *connection = NULL;
  793. smart_str hashed_details = {0};
  794. time_t timestamp;
  795. php_oci_spool *session_pool = NULL;
  796. bool use_spool = 1; /* Default is to use client-side session pool */
  797. bool ping_done = 0;
  798. ub2 charsetid = 0;
  799. ub2 charsetid_nls_lang = 0;
  800. if (session_mode & ~(OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
  801. php_error_docref(NULL, E_WARNING, "Invalid session mode specified (" ZEND_LONG_FMT ")", session_mode);
  802. return NULL;
  803. }
  804. if (session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) {
  805. if ((session_mode & OCI_SYSOPER) && (session_mode & OCI_SYSDBA)) {
  806. php_error_docref(NULL, E_WARNING, "OCI_SYSDBA and OCI_SYSOPER cannot be used together");
  807. return NULL;
  808. }
  809. if (session_mode & PHP_OCI_CRED_EXT) {
  810. #ifdef PHP_WIN32
  811. /* Disable external authentication on Windows as Impersonation is not yet handled.
  812. * TODO: Re-enable this once OCI provides capability.
  813. */
  814. php_error_docref(NULL, E_WARNING, "External Authentication is not supported on Windows");
  815. return NULL;
  816. #endif
  817. if (username_len != 1 || username[0] != '/' || password_len != 0) {
  818. php_error_docref(NULL, E_WARNING, "OCI_CRED_EXT can only be used with a username of \"/\" and a NULL password");
  819. return NULL;
  820. }
  821. }
  822. if (session_mode & (OCI_SYSOPER | OCI_SYSDBA)) {
  823. /* Increase security by not caching privileged oci_pconnect() connections. The
  824. * connection becomes equivalent to oci_connect() or oci_new_connect().
  825. */
  826. persistent = 0;
  827. if (!OCI_G(privileged_connect)) {
  828. php_error_docref(NULL, E_WARNING, "Privileged connect is disabled. Enable oci8.privileged_connect to be able to connect as SYSOPER or SYSDBA");
  829. return NULL;
  830. }
  831. }
  832. }
  833. /* Initialize global handles if they weren't initialized before */
  834. if (OCI_G(env) == NULL) {
  835. php_oci_init_global_handles();
  836. if (OCI_G(env) == NULL) {
  837. return NULL;
  838. }
  839. }
  840. /* We cannot use the new session create logic (OCISessionGet from
  841. * client-side session pool) when privileged connect or password
  842. * change is attempted or OCI_CRED_EXT mode is specified.
  843. * TODO: Re-enable this when OCI provides support.
  844. */
  845. if ((session_mode & (OCI_SYSOPER | OCI_SYSDBA | PHP_OCI_CRED_EXT)) || (new_password_len)) {
  846. use_spool = 0;
  847. }
  848. smart_str_appendl_ex(&hashed_details, "oci8***", sizeof("oci8***") - 1, 0);
  849. smart_str_appendl_ex(&hashed_details, username, username_len, 0);
  850. smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
  851. /* DRCP: connection_class is an attribute of a connection */
  852. if (OCI_G(connection_class)){
  853. smart_str_appendl_ex(&hashed_details, OCI_G(connection_class), strlen(OCI_G(connection_class)), 0);
  854. }
  855. smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
  856. /* Add edition attribute to the hash */
  857. if (OCI_G(edition)){
  858. smart_str_appendl_ex(&hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
  859. }
  860. smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
  861. if (password_len) {
  862. zend_ulong password_hash;
  863. password_hash = zend_hash_func(password, password_len);
  864. smart_str_append_unsigned_ex(&hashed_details, password_hash, 0);
  865. }
  866. smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
  867. if (dbname) {
  868. smart_str_appendl_ex(&hashed_details, dbname, dbname_len, 0);
  869. }
  870. smart_str_appendl_ex(&hashed_details, "**", sizeof("**") - 1, 0);
  871. if (charset && *charset) {
  872. PHP_OCI_CALL_RETURN(charsetid, OCINlsCharSetNameToId, (OCI_G(env), (CONST oratext *)charset));
  873. if (!charsetid) {
  874. php_error_docref(NULL, E_WARNING, "Invalid character set name: %s", charset);
  875. } else {
  876. smart_str_append_unsigned_ex(&hashed_details, charsetid, 0);
  877. }
  878. }
  879. /* use NLS_LANG if no or invalid charset specified */
  880. if (!charsetid) {
  881. size_t rsize = 0;
  882. sword result;
  883. PHP_OCI_CALL_RETURN(result, OCINlsEnvironmentVariableGet, (&charsetid_nls_lang, 0, OCI_NLS_CHARSET_ID, 0, &rsize));
  884. if (result != OCI_SUCCESS) {
  885. charsetid_nls_lang = 0;
  886. }
  887. smart_str_append_unsigned_ex(&hashed_details, charsetid_nls_lang, 0);
  888. }
  889. timestamp = time(NULL);
  890. smart_str_append_unsigned_ex(&hashed_details, session_mode, 0);
  891. if (persistent) {
  892. smart_str_appendl_ex(&hashed_details, "pc", sizeof("pc") - 1, 0);
  893. }
  894. smart_str_0(&hashed_details);
  895. /* make it lowercase */
  896. zend_str_tolower(ZSTR_VAL(hashed_details.s), ZSTR_LEN(hashed_details.s));
  897. if (!exclusive && !new_password) {
  898. bool found = 0;
  899. if (persistent && ((zvp = zend_hash_find(&EG(persistent_list), hashed_details.s))) != NULL) {
  900. zend_resource *le = Z_RES_P(zvp);
  901. found = 1;
  902. /* found */
  903. if (le->type == le_pconnection) {
  904. connection = (php_oci_connection *)le->ptr;
  905. }
  906. } else if (!persistent && ((zvp = zend_hash_find(&EG(regular_list), hashed_details.s)) != NULL)) {
  907. le = Z_RES_P(zvp);
  908. found = 1;
  909. if (le->type == le_index_ptr) {
  910. zend_resource *ptr;
  911. ptr = (zend_resource *) le->ptr;
  912. if (ptr && (ptr->type == le_connection)) {
  913. connection = (php_oci_connection *)ptr->ptr;
  914. }
  915. }
  916. }
  917. #ifdef HAVE_OCI8_DTRACE
  918. if (DTRACE_OCI8_CONNECT_LOOKUP_ENABLED()) {
  919. DTRACE_OCI8_CONNECT_LOOKUP(connection, connection && connection->is_stub ? 1 : 0);
  920. }
  921. #endif /* HAVE_OCI8_DTRACE */
  922. /* If we got a pconnection stub, then 'load'(OCISessionGet) the real connection from its
  923. * private spool A connection is a stub if it is only a cached structure and the real
  924. * connection is released to its underlying private session pool. We currently do not have
  925. * stub support for non-persistent conns.
  926. *
  927. * TODO: put in negative code for non-persistent stubs
  928. */
  929. if (connection && connection->is_persistent && connection->is_stub) {
  930. if (php_oci_create_session(connection, NULL, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
  931. smart_str_free(&hashed_details);
  932. zend_hash_del(&EG(persistent_list), connection->hash_key);
  933. return NULL;
  934. }
  935. /* We do the ping in php_oci_create_session, no need to ping again below */
  936. ping_done = 1;
  937. }
  938. if (connection) {
  939. if (connection->is_open) {
  940. /* found an open connection. now ping it */
  941. if (connection->is_persistent) {
  942. /* Check connection liveness in the following order:
  943. * 1) always check OCI_ATTR_SERVER_STATUS
  944. * 2) see if it's time to ping it
  945. * 3) ping it if needed
  946. */
  947. if (php_oci_connection_status(connection)) {
  948. /* Only ping if:
  949. *
  950. * 1) next_ping > 0, which means that ping_interval is not -1 (aka "Off")
  951. *
  952. * 2) current_timestamp > next_ping, which means "it's time to check if it's
  953. * still alive"
  954. */
  955. if (!ping_done && (*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp)) && !php_oci_connection_ping(connection)) {
  956. /* server died */
  957. } else {
  958. php_oci_connection *tmp = (php_oci_connection *) NULL;
  959. zval *tmp_val = (zval *) NULL;
  960. /* okay, the connection is open and the server is still alive */
  961. connection->used_this_request = 1;
  962. if (connection->id) {
  963. tmp_val = zend_hash_index_find(&EG(regular_list), connection->id->handle);
  964. if ((tmp_val != NULL) && (Z_TYPE_P(tmp_val) == IS_RESOURCE)) {
  965. tmp = Z_RES_VAL_P(tmp_val);
  966. }
  967. if ((tmp_val != NULL) && (tmp != NULL) &&
  968. (ZSTR_LEN(tmp->hash_key) == ZSTR_LEN(hashed_details.s)) &&
  969. (memcmp(ZSTR_VAL(tmp->hash_key), ZSTR_VAL(hashed_details.s),
  970. ZSTR_LEN(tmp->hash_key)) == 0)) {
  971. connection = tmp;
  972. GC_ADDREF(connection->id);
  973. }
  974. } else {
  975. PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
  976. /* Persistent connections: For old close semantics we artificially
  977. * bump up the refcount to prevent the non-persistent destructor
  978. * from getting called until request shutdown. The refcount is
  979. * decremented in the persistent helper
  980. */
  981. if (OCI_G(old_oci_close_semantics)) {
  982. GC_ADDREF(connection->id);
  983. }
  984. }
  985. smart_str_free(&hashed_details);
  986. return connection;
  987. }
  988. }
  989. /* server died */
  990. } else {
  991. /* we do not ping non-persistent connections */
  992. smart_str_free(&hashed_details);
  993. GC_ADDREF(connection->id);
  994. return connection;
  995. }
  996. } /* is_open is true? */
  997. /* Server died - connection not usable. The is_open=true can also fall through to here,
  998. * if ping fails
  999. */
  1000. if (persistent){
  1001. connection->is_open = 0;
  1002. connection->used_this_request = 1;
  1003. /* We have to do a hash_del but need to preserve the resource if there is a positive
  1004. * refcount. Set the data pointer in the list entry to NULL
  1005. */
  1006. if (connection == connection->id->ptr && le) {
  1007. le->ptr = NULL;
  1008. }
  1009. zend_hash_del(&EG(persistent_list), hashed_details.s);
  1010. } else {
  1011. /* We only remove the hash entry. The resource and the list entry with its pointer
  1012. * to the resource are still intact
  1013. */
  1014. zend_hash_del(&EG(regular_list), hashed_details.s);
  1015. }
  1016. connection = NULL;
  1017. } else if (found) {
  1018. /* found something, but it's not a connection, delete it */
  1019. if (persistent) {
  1020. zend_hash_del(&EG(persistent_list), hashed_details.s);
  1021. } else {
  1022. zend_hash_del(&EG(regular_list), hashed_details.s);
  1023. }
  1024. }
  1025. }
  1026. /* Check if we have reached max_persistent. If so, try to remove a few timed-out connections. As
  1027. * a last resort, return a non-persistent connection.
  1028. */
  1029. if (persistent) {
  1030. bool alloc_non_persistent = 0;
  1031. if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
  1032. /* try to find an idle connection and kill it */
  1033. zend_hash_apply(&EG(persistent_list), php_oci_persistent_helper);
  1034. if (OCI_G(max_persistent) != -1 && OCI_G(num_persistent) >= OCI_G(max_persistent)) {
  1035. /* all persistent connactions are in use, fallback to non-persistent connection creation */
  1036. php_error_docref(NULL, E_NOTICE, "Too many open persistent connections (" ZEND_LONG_FMT ")", OCI_G(num_persistent));
  1037. alloc_non_persistent = 1;
  1038. }
  1039. }
  1040. if (alloc_non_persistent) {
  1041. connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
  1042. connection->hash_key = zend_string_dup(hashed_details.s, 0);
  1043. connection->is_persistent = 0;
  1044. ZVAL_UNDEF(&connection->taf_callback);
  1045. #ifdef HAVE_OCI8_DTRACE
  1046. connection->client_id = NULL;
  1047. #endif
  1048. } else {
  1049. connection = (php_oci_connection *) calloc(1, sizeof(php_oci_connection));
  1050. if (connection == NULL) {
  1051. return NULL;
  1052. }
  1053. connection->hash_key = zend_string_dup(hashed_details.s, 1);
  1054. if (connection->hash_key == NULL) {
  1055. free(connection);
  1056. return NULL;
  1057. }
  1058. connection->is_persistent = 1;
  1059. ZVAL_UNDEF(&connection->taf_callback);
  1060. #ifdef HAVE_OCI8_DTRACE
  1061. connection->client_id = NULL;
  1062. #endif
  1063. }
  1064. } else {
  1065. connection = (php_oci_connection *) ecalloc(1, sizeof(php_oci_connection));
  1066. connection->hash_key = zend_string_dup(hashed_details.s, 0);
  1067. connection->is_persistent = 0;
  1068. ZVAL_UNDEF(&connection->taf_callback);
  1069. #ifdef HAVE_OCI8_DTRACE
  1070. connection->client_id = NULL;
  1071. #endif
  1072. }
  1073. /* {{{ Get the session pool that suits this connection request from the persistent list. This
  1074. * step is only for non-persistent connections as persistent connections have private session
  1075. * pools. Non-persistent conns use shared session pool to allow for optimizations such as
  1076. * caching the physical connection (for DRCP) even when the non-persistent php connection is
  1077. * destroyed.
  1078. *
  1079. * TODO: Unconditionally do this once OCI provides extended OCISessionGet capability
  1080. */
  1081. if (use_spool && !connection->is_persistent) {
  1082. if ((session_pool = php_oci_get_spool(username, username_len, password, password_len, dbname, dbname_len, charsetid ? charsetid:charsetid_nls_lang))==NULL)
  1083. {
  1084. php_oci_connection_close(connection);
  1085. smart_str_free(&hashed_details);
  1086. return NULL;
  1087. }
  1088. }
  1089. /* }}} */
  1090. connection->idle_expiry = (OCI_G(persistent_timeout) > 0) ? (timestamp + OCI_G(persistent_timeout)) : 0;
  1091. /* Mark password as unchanged by PHP during the duration of the database session */
  1092. connection->passwd_changed = 0;
  1093. smart_str_free(&hashed_details);
  1094. if (charsetid) {
  1095. connection->charset = charsetid;
  1096. } else {
  1097. connection->charset = charsetid_nls_lang;
  1098. }
  1099. /* Old session creation semantics when session pool cannot be used Eg: privileged
  1100. * connect/password change
  1101. */
  1102. if (!use_spool) {
  1103. if (php_oci_old_create_session(connection, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
  1104. php_oci_connection_close(connection);
  1105. return NULL;
  1106. }
  1107. } else {
  1108. /* create using the client-side session pool */
  1109. if (php_oci_create_session(connection, session_pool, dbname, dbname_len, username, username_len, password, password_len, new_password, new_password_len, (int) session_mode)) {
  1110. php_oci_connection_close(connection);
  1111. return NULL;
  1112. }
  1113. }
  1114. /* Mark it as open */
  1115. connection->is_open = 1;
  1116. /* add to the appropriate hash */
  1117. if (connection->is_persistent) {
  1118. connection->used_this_request = 1;
  1119. PHP_OCI_REGISTER_RESOURCE(connection, le_pconnection);
  1120. /* Persistent connections: For old close semantics we artificially bump up the refcount to
  1121. * prevent the non-persistent destructor from getting called until request shutdown. The
  1122. * refcount is decremented in the persistent helper
  1123. */
  1124. if (OCI_G(old_oci_close_semantics)) {
  1125. GC_ADDREF(connection->id);
  1126. }
  1127. zend_register_persistent_resource_ex(connection->hash_key, connection, le_pconnection);
  1128. OCI_G(num_persistent)++;
  1129. OCI_G(num_links)++;
  1130. } else if (!exclusive) {
  1131. PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
  1132. new_le.ptr = connection->id;
  1133. new_le.type = le_index_ptr;
  1134. zend_hash_update_mem(&EG(regular_list), connection->hash_key, (void *)&new_le, sizeof(zend_resource));
  1135. OCI_G(num_links)++;
  1136. } else {
  1137. PHP_OCI_REGISTER_RESOURCE(connection, le_connection);
  1138. OCI_G(num_links)++;
  1139. }
  1140. #ifdef HAVE_OCI8_DTRACE
  1141. if (DTRACE_OCI8_CONNECT_TYPE_ENABLED()) {
  1142. DTRACE_OCI8_CONNECT_TYPE(connection->is_persistent ? 1 : 0, exclusive ? 1 : 0, connection, OCI_G(num_persistent), OCI_G(num_links));
  1143. }
  1144. #endif /* HAVE_OCI8_DTRACE */
  1145. return connection;
  1146. }
  1147. /* }}} */
  1148. /* {{{ php_oci_connection_ping()
  1149. *
  1150. * Ping connection. Uses OCIPing() or OCIServerVersion() depending on the Oracle Client version
  1151. */
  1152. static int php_oci_connection_ping(php_oci_connection *connection)
  1153. {
  1154. sword errstatus;
  1155. #if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))))
  1156. char version[256];
  1157. #endif
  1158. OCI_G(errcode) = 0; /* assume ping is successful */
  1159. /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation)
  1160. * such as from Pre-10.1 servers, the error is still from the server and we would have
  1161. * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for
  1162. * Pre-10.2 clients
  1163. */
  1164. #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */
  1165. PHP_OCI_CALL_RETURN(errstatus, OCIPing, (connection->svc, OCI_G(err), OCI_DEFAULT));
  1166. #else
  1167. /* use good old OCIServerVersion() */
  1168. PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, OCI_G(err), (text *)version, sizeof(version), OCI_HTYPE_SVCCTX));
  1169. #endif
  1170. if (errstatus == OCI_SUCCESS) {
  1171. return 1;
  1172. } else {
  1173. sb4 error_code = 0;
  1174. text tmp_buf[PHP_OCI_ERRBUF_LEN];
  1175. /* Treat ORA-1010 as a successful Ping */
  1176. OCIErrorGet(OCI_G(err), (ub4)1, NULL, &error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR);
  1177. if (error_code == 1010) {
  1178. return 1;
  1179. }
  1180. OCI_G(errcode) = error_code;
  1181. }
  1182. return 0;
  1183. }
  1184. /* }}} */
  1185. /* {{{ php_oci_connection_status()
  1186. *
  1187. * Check connection status (pre-ping check)
  1188. */
  1189. static int php_oci_connection_status(php_oci_connection *connection)
  1190. {
  1191. ub4 ss = OCI_SERVER_NOT_CONNECTED;
  1192. sword errstatus;
  1193. /* get OCI_ATTR_SERVER_STATUS */
  1194. PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)connection->server, OCI_HTYPE_SERVER, (dvoid *)&ss, (ub4 *)0, OCI_ATTR_SERVER_STATUS, OCI_G(err)));
  1195. if (errstatus == OCI_SUCCESS && ss == OCI_SERVER_NORMAL) {
  1196. return 1;
  1197. }
  1198. /* ignore errors here, just return failure */
  1199. return 0;
  1200. }
  1201. /* }}} */
  1202. /* {{{ php_oci_connection_rollback()
  1203. *
  1204. * Rollback connection
  1205. */
  1206. int php_oci_connection_rollback(php_oci_connection *connection)
  1207. {
  1208. sword errstatus;
  1209. PHP_OCI_CALL_RETURN(errstatus, OCITransRollback, (connection->svc, connection->err, (ub4) 0));
  1210. connection->rb_on_disconnect = 0;
  1211. if (errstatus != OCI_SUCCESS) {
  1212. connection->errcode = php_oci_error(connection->err, errstatus);
  1213. PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
  1214. return 1;
  1215. }
  1216. connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
  1217. return 0;
  1218. }
  1219. /* }}} */
  1220. /* {{{ php_oci_connection_commit()
  1221. *
  1222. * Commit connection
  1223. */
  1224. int php_oci_connection_commit(php_oci_connection *connection)
  1225. {
  1226. sword errstatus;
  1227. PHP_OCI_CALL_RETURN(errstatus, OCITransCommit, (connection->svc, connection->err, (ub4) 0));
  1228. connection->rb_on_disconnect = 0;
  1229. if (errstatus != OCI_SUCCESS) {
  1230. connection->errcode = php_oci_error(connection->err, errstatus);
  1231. PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
  1232. return 1;
  1233. }
  1234. connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
  1235. return 0;
  1236. }
  1237. /* }}} */
  1238. /* {{{ php_oci_connection_close()
  1239. *
  1240. * Close the connection and free all its resources
  1241. */
  1242. static int php_oci_connection_close(php_oci_connection *connection)
  1243. {
  1244. int result = 0;
  1245. bool in_call_save = OCI_G(in_call);
  1246. #ifdef HAVE_OCI8_DTRACE
  1247. if (DTRACE_OCI8_CONNECTION_CLOSE_ENABLED()) {
  1248. DTRACE_OCI8_CONNECTION_CLOSE(connection);
  1249. }
  1250. #endif /* HAVE_OCI8_DTRACE */
  1251. if (!connection->is_stub) {
  1252. /* Release resources associated with connection */
  1253. php_oci_connection_release(connection);
  1254. }
  1255. if (!connection->using_spool && connection->svc) {
  1256. PHP_OCI_CALL(OCISessionEnd, (connection->svc, connection->err, connection->session, (ub4) 0));
  1257. }
  1258. if (connection->err) {
  1259. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->err, (ub4) OCI_HTYPE_ERROR));
  1260. }
  1261. if (connection->authinfo) {
  1262. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->authinfo, (ub4) OCI_HTYPE_AUTHINFO));
  1263. }
  1264. /* No Handlefrees for session pool connections */
  1265. if (!connection->using_spool) {
  1266. if (connection->session) {
  1267. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->session, OCI_HTYPE_SESSION));
  1268. }
  1269. if (connection->is_attached) {
  1270. PHP_OCI_CALL(OCIServerDetach, (connection->server, OCI_G(err), OCI_DEFAULT));
  1271. }
  1272. if (connection->svc) {
  1273. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX));
  1274. }
  1275. if (connection->server) {
  1276. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->server, (ub4) OCI_HTYPE_SERVER));
  1277. }
  1278. if (connection->env) {
  1279. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) connection->env, OCI_HTYPE_ENV));
  1280. }
  1281. } else if (connection->private_spool) {
  1282. /* Keep this as the last member to be freed, as there are dependencies
  1283. * (like env) on the session pool
  1284. */
  1285. php_oci_spool_close(connection->private_spool);
  1286. connection->private_spool = NULL;
  1287. }
  1288. if (GC_REFCOUNT(connection->hash_key) >= 2) {
  1289. zend_hash_del(&EG(regular_list), connection->hash_key);
  1290. }
  1291. if (connection->hash_key) {
  1292. pefree(connection->hash_key, connection->is_persistent);
  1293. connection->hash_key = NULL;
  1294. }
  1295. #ifdef HAVE_OCI8_DTRACE
  1296. if (connection->client_id) {
  1297. pefree(connection->client_id, connection->is_persistent);
  1298. connection->client_id = NULL;
  1299. }
  1300. #endif /* HAVE_OCI8_DTRACE */
  1301. if (!Z_ISUNDEF(connection->taf_callback)) {
  1302. /* If it's NULL, then its value should be freed already */
  1303. if (!Z_ISNULL(connection->taf_callback)) {
  1304. zval_ptr_dtor(&connection->taf_callback);
  1305. }
  1306. ZVAL_UNDEF(&connection->taf_callback);
  1307. }
  1308. pefree(connection, connection->is_persistent);
  1309. connection = NULL;
  1310. OCI_G(in_call) = in_call_save;
  1311. return result;
  1312. }
  1313. /* }}} */
  1314. /* {{{ php_oci_connection_release()
  1315. *
  1316. * Release the connection's resources. This involves freeing descriptors and rolling back
  1317. * transactions, setting timeout-related parameters etc. For session-pool using connections, the
  1318. * underlying connection is released to its session pool.
  1319. */
  1320. int php_oci_connection_release(php_oci_connection *connection)
  1321. {
  1322. int result = 0;
  1323. bool in_call_save = OCI_G(in_call);
  1324. time_t timestamp = time(NULL);
  1325. if (connection->is_stub) {
  1326. return 0;
  1327. }
  1328. if (connection->descriptors) {
  1329. php_oci_connection_descriptors_free(connection);
  1330. }
  1331. if (connection->svc) {
  1332. /* rollback outstanding transactions */
  1333. if (connection->rb_on_disconnect) {
  1334. if (php_oci_connection_rollback(connection)) {
  1335. /* rollback failed */
  1336. result = 1;
  1337. }
  1338. }
  1339. }
  1340. if (OCI_G(persistent_timeout) > 0) {
  1341. connection->idle_expiry = timestamp + OCI_G(persistent_timeout);
  1342. }
  1343. /* We may have half-cooked connections to clean up */
  1344. if (connection->next_pingp) {
  1345. if (OCI_G(ping_interval) >= 0) {
  1346. *(connection->next_pingp) = timestamp + OCI_G(ping_interval);
  1347. } else {
  1348. /* ping_interval is -1 */
  1349. *(connection->next_pingp) = 0;
  1350. }
  1351. }
  1352. /* Release the session (stubs are filtered out at the beginning)*/
  1353. if (connection->using_spool) {
  1354. ub4 rlsMode = OCI_DEFAULT;
  1355. if (result) {
  1356. rlsMode |= OCI_SESSRLS_DROPSESS;
  1357. }
  1358. /* Sessions for non-persistent connections should be dropped. For 11 and above, the session
  1359. * pool has its own mechanism for doing so for purity NEW connections. We need to do so
  1360. * explicitly for 10.2 and earlier.
  1361. */
  1362. #if (!(OCI_MAJOR_VERSION >= 11))
  1363. if (!connection->is_persistent) {
  1364. rlsMode |= OCI_SESSRLS_DROPSESS;
  1365. }
  1366. #endif
  1367. if (connection->svc) {
  1368. PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,
  1369. 0, rlsMode));
  1370. }
  1371. /* It no longer has relation with the database session. However authinfo and env are
  1372. * cached
  1373. */
  1374. connection->svc = NULL;
  1375. connection->server = NULL;
  1376. connection->session = NULL;
  1377. connection->is_attached = connection->is_open = connection->rb_on_disconnect = connection->used_this_request = 0;
  1378. connection->is_stub = 1;
  1379. /* Cut the link between the connection structure and the time_t structure allocated within
  1380. * the OCI session
  1381. */
  1382. connection->next_pingp = NULL;
  1383. #ifdef HAVE_OCI8_DTRACE
  1384. if (connection->client_id) {
  1385. pefree(connection->client_id, connection->is_persistent);
  1386. connection->client_id = NULL;
  1387. }
  1388. #endif /* HAVE_OCI8_DTRACE */
  1389. }
  1390. /* Always set id to null, so next time a new resource is being registered. */
  1391. connection->id = NULL;
  1392. OCI_G(in_call) = in_call_save;
  1393. return result;
  1394. }
  1395. /* }}} */
  1396. /* {{{ php_oci_password_change()
  1397. *
  1398. * Change password for the user with the username given
  1399. */
  1400. int php_oci_password_change(php_oci_connection *connection, char *user, int user_len, char *pass_old, int pass_old_len, char *pass_new, int pass_new_len)
  1401. {
  1402. sword errstatus;
  1403. PHP_OCI_CALL_RETURN(errstatus, OCIPasswordChange, (connection->svc, connection->err, (text *)user, user_len, (text *)pass_old, pass_old_len, (text *)pass_new, pass_new_len, OCI_DEFAULT));
  1404. if (errstatus != OCI_SUCCESS) {
  1405. connection->errcode = php_oci_error(connection->err, errstatus);
  1406. PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
  1407. return 1;
  1408. }
  1409. connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
  1410. connection->passwd_changed = 1;
  1411. return 0;
  1412. }
  1413. /* }}} */
  1414. /* {{{ php_oci_client_get_version()
  1415. *
  1416. * Get Oracle client library version
  1417. */
  1418. void php_oci_client_get_version(char *version, size_t version_size)
  1419. {
  1420. #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIClientVersion only available 10.2 onwards */
  1421. sword major_version = 0;
  1422. sword minor_version = 0;
  1423. sword update_num = 0;
  1424. sword patch_num = 0;
  1425. sword port_update_num = 0;
  1426. PHP_OCI_CALL(OCIClientVersion, (&major_version, &minor_version, &update_num, &patch_num, &port_update_num));
  1427. snprintf(version, version_size, "%d.%d.%d.%d.%d", major_version, minor_version, update_num, patch_num, port_update_num);
  1428. #else
  1429. memcpy(version, "Unknown", sizeof("Unknown"));
  1430. #endif
  1431. }
  1432. /* }}} */
  1433. /* {{{ php_oci_server_get_version()
  1434. *
  1435. * Get Oracle server version
  1436. */
  1437. int php_oci_server_get_version(php_oci_connection *connection, char *version, size_t version_size)
  1438. {
  1439. sword errstatus;
  1440. PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, connection->err, (text *)version, (ub4) version_size, OCI_HTYPE_SVCCTX));
  1441. if (errstatus != OCI_SUCCESS) {
  1442. connection->errcode = php_oci_error(connection->err, errstatus);
  1443. PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
  1444. return 1;
  1445. }
  1446. return 0;
  1447. }
  1448. /* }}} */
  1449. /* {{{ php_oci_column_to_zval()
  1450. *
  1451. * Convert php_oci_out_column struct into zval
  1452. */
  1453. int php_oci_column_to_zval(php_oci_out_column *column, zval *value, int mode)
  1454. {
  1455. php_oci_descriptor *descriptor;
  1456. ub4 lob_length;
  1457. int column_size;
  1458. char *lob_buffer = (char *)0;
  1459. int lob_fetch_status;
  1460. if (column->indicator == -1) { /* column is NULL */
  1461. ZVAL_NULL(value);
  1462. return 0;
  1463. }
  1464. if (column->is_cursor) { /* REFCURSOR -> simply return the statement id */
  1465. ZVAL_RES(value, column->stmtid);
  1466. GC_ADDREF(column->stmtid);
  1467. } else if (column->is_descr) {
  1468. if (column->data_type != SQLT_RDD) {
  1469. /* reset descriptor's length */
  1470. descriptor = (php_oci_descriptor *) column->descid->ptr;
  1471. if (!descriptor) {
  1472. php_error_docref(NULL, E_WARNING, "Unable to find LOB descriptor #" ZEND_LONG_FMT, column->descid->handle);
  1473. return 1;
  1474. }
  1475. descriptor->lob_size = -1;
  1476. descriptor->lob_current_position = 0;
  1477. descriptor->buffering = 0;
  1478. }
  1479. if (column->data_type != SQLT_RDD && (mode & PHP_OCI_RETURN_LOBS)) {
  1480. /* PHP_OCI_RETURN_LOBS means that we want the content of the LOB back instead of the locator */
  1481. if (column->chunk_size)
  1482. descriptor->chunk_size = column->chunk_size;
  1483. lob_fetch_status = php_oci_lob_read(descriptor, -1, 0, &lob_buffer, &lob_length);
  1484. if (descriptor->chunk_size) /* Cache the chunk_size to avoid recalling OCILobGetChunkSize */
  1485. column->chunk_size = descriptor->chunk_size;
  1486. php_oci_temp_lob_close(descriptor);
  1487. if (lob_fetch_status) {
  1488. ZVAL_FALSE(value);
  1489. return 1;
  1490. } else {
  1491. if (lob_length > 0) {
  1492. ZVAL_STRINGL(value, lob_buffer, lob_length);
  1493. } else {
  1494. ZVAL_EMPTY_STRING(value);
  1495. }
  1496. if (lob_buffer)
  1497. efree(lob_buffer);
  1498. return 0;
  1499. }
  1500. } else {
  1501. /* return the locator */
  1502. object_init_ex(value, oci_lob_class_entry_ptr);
  1503. add_property_resource(value, "descriptor", column->descid);
  1504. GC_ADDREF(column->descid);
  1505. }
  1506. } else {
  1507. switch (column->retcode) {
  1508. case 0:
  1509. /* intact value */
  1510. if (column->piecewise) {
  1511. column_size = column->retlen4;
  1512. } else {
  1513. column_size = column->retlen;
  1514. }
  1515. break;
  1516. default:
  1517. ZVAL_FALSE(value);
  1518. return 0;
  1519. }
  1520. ZVAL_STRINGL(value, column->data, column_size);
  1521. }
  1522. return 0;
  1523. }
  1524. /* }}} */
  1525. /* {{{ php_oci_fetch_row()
  1526. *
  1527. * Fetch the next row from the given statement
  1528. * Has logic for Oracle 12c Implicit Result Sets
  1529. */
  1530. void php_oci_fetch_row (INTERNAL_FUNCTION_PARAMETERS, int mode, int expected_args)
  1531. {
  1532. zval *z_statement, *array;
  1533. zval *placeholder = (zval*) NULL;
  1534. /* zend_array *temp_array = (zend_array *) NULL;*/
  1535. php_oci_statement *statement; /* statement that will be fetched from */
  1536. #if (OCI_MAJOR_VERSION >= 12)
  1537. php_oci_statement *invokedstatement; /* statement this function was invoked with */
  1538. #endif /* OCI_MAJOR_VERSION */
  1539. php_oci_out_column *column;
  1540. ub4 nrows = 1;
  1541. int i;
  1542. zend_long fetch_mode = 0;
  1543. if (expected_args > 2) {
  1544. /* only for ocifetchinto BC */
  1545. ZEND_PARSE_PARAMETERS_START(2, 3)
  1546. Z_PARAM_RESOURCE(z_statement)
  1547. Z_PARAM_ZVAL(array)
  1548. Z_PARAM_OPTIONAL
  1549. Z_PARAM_LONG(fetch_mode)
  1550. ZEND_PARSE_PARAMETERS_END();
  1551. if (ZEND_NUM_ARGS() == 2) {
  1552. fetch_mode = mode;
  1553. }
  1554. if (Z_ISREF_P(array))
  1555. placeholder = Z_REFVAL_P(array);
  1556. else
  1557. placeholder = array;
  1558. } else if (expected_args == 2) {
  1559. /* only for oci_fetch_array() */
  1560. ZEND_PARSE_PARAMETERS_START(1, 2)
  1561. Z_PARAM_RESOURCE(z_statement)
  1562. Z_PARAM_OPTIONAL
  1563. Z_PARAM_LONG(fetch_mode)
  1564. ZEND_PARSE_PARAMETERS_END();
  1565. if (ZEND_NUM_ARGS() == 1) {
  1566. fetch_mode = mode;
  1567. }
  1568. } else {
  1569. /* for all oci_fetch_*() */
  1570. ZEND_PARSE_PARAMETERS_START(1, 1)
  1571. Z_PARAM_RESOURCE(z_statement)
  1572. ZEND_PARSE_PARAMETERS_END();
  1573. fetch_mode = mode;
  1574. }
  1575. if (!(fetch_mode & PHP_OCI_NUM) && !(fetch_mode & PHP_OCI_ASSOC)) {
  1576. /* none of the modes present, use the default one */
  1577. if (mode & PHP_OCI_ASSOC) {
  1578. fetch_mode |= PHP_OCI_ASSOC;
  1579. }
  1580. if (mode & PHP_OCI_NUM) {
  1581. fetch_mode |= PHP_OCI_NUM;
  1582. }
  1583. }
  1584. #if (OCI_MAJOR_VERSION < 12)
  1585. PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement);
  1586. if (php_oci_statement_fetch(statement, nrows)) {
  1587. RETURN_FALSE; /* end of fetch */
  1588. }
  1589. #else /* OCI_MAJOR_VERSION */
  1590. PHP_OCI_ZVAL_TO_STATEMENT(z_statement, invokedstatement);
  1591. if (invokedstatement->impres_flag == PHP_OCI_IMPRES_NO_CHILDREN ||
  1592. invokedstatement->impres_flag == PHP_OCI_IMPRES_IS_CHILD) {
  1593. /* Already know there are no Implicit Result Sets */
  1594. statement = invokedstatement;
  1595. } else if (invokedstatement->impres_flag == PHP_OCI_IMPRES_HAS_CHILDREN) {
  1596. /* Previously saw an Implicit Result Set in an earlier invocation of php_oci_fetch_row */
  1597. statement = (php_oci_statement *)invokedstatement->impres_child_stmt;
  1598. } else {
  1599. sword errstatus;
  1600. /* Check for an Implicit Result Set on this statement handle */
  1601. PHP_OCI_CALL_RETURN(errstatus, OCIAttrGet, ((dvoid *)invokedstatement->stmt, OCI_HTYPE_STMT,
  1602. (dvoid *) &invokedstatement->impres_count,
  1603. (ub4 *)NULL, OCI_ATTR_IMPLICIT_RESULT_COUNT, invokedstatement->err));
  1604. if (errstatus) {
  1605. RETURN_FALSE;
  1606. }
  1607. if (invokedstatement->impres_count > 0) {
  1608. /* Make it so the fetch occurs on the first Implicit Result Set */
  1609. statement = php_oci_get_implicit_resultset(invokedstatement);
  1610. if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT))
  1611. RETURN_FALSE;
  1612. invokedstatement->impres_count--;
  1613. invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
  1614. invokedstatement->impres_flag = PHP_OCI_IMPRES_HAS_CHILDREN;
  1615. } else {
  1616. statement = invokedstatement; /* didn't find Implicit Result Sets */
  1617. invokedstatement->impres_flag = PHP_OCI_IMPRES_NO_CHILDREN; /* Don't bother checking again */
  1618. }
  1619. }
  1620. if (php_oci_statement_fetch(statement, nrows)) {
  1621. /* End of fetch */
  1622. if (invokedstatement->impres_count > 0) {
  1623. /* Check next Implicit Result Set */
  1624. statement = php_oci_get_implicit_resultset(invokedstatement);
  1625. if (!statement || php_oci_statement_execute(statement, (ub4)OCI_DEFAULT))
  1626. RETURN_FALSE;
  1627. invokedstatement->impres_count--;
  1628. invokedstatement->impres_child_stmt = (struct php_oci_statement *)statement;
  1629. if (php_oci_statement_fetch(statement, nrows)) {
  1630. /* End of all fetches */
  1631. RETURN_FALSE;
  1632. }
  1633. } else {
  1634. RETURN_FALSE;
  1635. }
  1636. }
  1637. #endif /* OCI_MAJOR_VERSION */
  1638. if (placeholder == NULL) {
  1639. placeholder = return_value;
  1640. } else {
  1641. zval_ptr_dtor(placeholder);
  1642. }
  1643. array_init(placeholder);
  1644. for (i = 0; i < statement->ncolumns; i++) {
  1645. column = php_oci_statement_get_column(statement, i + 1, NULL, 0);
  1646. if (column == NULL) {
  1647. continue;
  1648. }
  1649. if ((column->indicator == -1) && ((fetch_mode & PHP_OCI_RETURN_NULLS) == 0)) {
  1650. continue;
  1651. }
  1652. if (!(column->indicator == -1)) {
  1653. zval element;
  1654. php_oci_column_to_zval(column, &element, (int) fetch_mode);
  1655. if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
  1656. add_index_zval(placeholder, i, &element);
  1657. }
  1658. if (fetch_mode & PHP_OCI_ASSOC) {
  1659. if (fetch_mode & PHP_OCI_NUM) {
  1660. Z_TRY_ADDREF_P(&element);
  1661. }
  1662. add_assoc_zval(placeholder, column->name, &element);
  1663. }
  1664. } else {
  1665. if (fetch_mode & PHP_OCI_NUM || !(fetch_mode & PHP_OCI_ASSOC)) {
  1666. add_index_null(placeholder, i);
  1667. }
  1668. if (fetch_mode & PHP_OCI_ASSOC) {
  1669. add_assoc_null(placeholder, column->name);
  1670. }
  1671. }
  1672. }
  1673. if (expected_args > 2) {
  1674. RETURN_LONG(statement->ncolumns);
  1675. }
  1676. }
  1677. /* }}} */
  1678. /* {{{ php_oci_persistent_helper()
  1679. *
  1680. * Helper function to close/rollback persistent connections at the end of request. A return value of
  1681. * 1 indicates that the connection is to be destroyed
  1682. */
  1683. static int php_oci_persistent_helper(zval *zv)
  1684. {
  1685. zend_resource *le = Z_RES_P(zv);
  1686. time_t timestamp;
  1687. php_oci_connection *connection;
  1688. timestamp = time(NULL);
  1689. /* Persistent connection stubs are also counted as they have private session pools */
  1690. if (le->type == le_pconnection) {
  1691. connection = (php_oci_connection *)le->ptr;
  1692. /* Remove TAF callback function as it's bound to current request */
  1693. if (connection->used_this_request && !Z_ISUNDEF(connection->taf_callback) && !Z_ISNULL(connection->taf_callback)) {
  1694. php_oci_unregister_taf_callback(connection);
  1695. }
  1696. if (!connection->used_this_request && OCI_G(persistent_timeout) != -1) {
  1697. #ifdef HAVE_OCI8_DTRACE
  1698. if (DTRACE_OCI8_CONNECT_EXPIRY_ENABLED()) {
  1699. DTRACE_OCI8_CONNECT_EXPIRY(connection, connection->is_stub ? 1 : 0, (long)connection->idle_expiry, (long)timestamp);
  1700. }
  1701. #endif /* HAVE_OCI8_DTRACE */
  1702. if (connection->idle_expiry < timestamp) {
  1703. /* connection has timed out */
  1704. return ZEND_HASH_APPLY_REMOVE;
  1705. }
  1706. }
  1707. }
  1708. return ZEND_HASH_APPLY_KEEP;
  1709. }
  1710. /* }}} */
  1711. /* {{{ php_oci_create_spool()
  1712. *
  1713. * Create(alloc + Init) Session pool for the given dbname and charsetid
  1714. */
  1715. static php_oci_spool *php_oci_create_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, zend_string *hash_key, int charsetid)
  1716. {
  1717. php_oci_spool *session_pool = NULL;
  1718. bool iserror = 0;
  1719. ub4 poolmode = OCI_DEFAULT; /* Mode to be passed to OCISessionPoolCreate */
  1720. OCIAuthInfo *spoolAuth = NULL;
  1721. sword errstatus;
  1722. /* Allocate sessionpool out of persistent memory */
  1723. session_pool = (php_oci_spool *) calloc(1, sizeof(php_oci_spool));
  1724. if (session_pool == NULL) {
  1725. iserror = 1;
  1726. goto exit_create_spool;
  1727. }
  1728. /* Populate key if passed */
  1729. if (hash_key && (ZSTR_LEN(hash_key) > 0)) {
  1730. session_pool->spool_hash_key = zend_string_dup(hash_key, 1);
  1731. if (session_pool->spool_hash_key == NULL) {
  1732. iserror = 1;
  1733. goto exit_create_spool;
  1734. }
  1735. }
  1736. /* Create the session pool's env */
  1737. if (!(session_pool->env = php_oci_create_env(charsetid))) {
  1738. iserror = 1;
  1739. goto exit_create_spool;
  1740. }
  1741. /* Allocate the pool handle */
  1742. PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **) &session_pool->poolh, OCI_HTYPE_SPOOL, (size_t) 0, (dvoid **) 0));
  1743. if (errstatus != OCI_SUCCESS) {
  1744. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1745. iserror = 1;
  1746. goto exit_create_spool;
  1747. }
  1748. /* Allocate the session pool error handle - This only for use in the destructor, as there is a
  1749. * generic bug which can free up the OCI_G(err) variable before destroying connections. We
  1750. * cannot use this for other roundtrip calls as there is no way the user can access this error
  1751. */
  1752. PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, ((dvoid *) session_pool->env, (dvoid **)&(session_pool->err), (ub4) OCI_HTYPE_ERROR,(size_t) 0, (dvoid **) 0));
  1753. if (errstatus != OCI_SUCCESS) {
  1754. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1755. iserror = 1;
  1756. goto exit_create_spool;
  1757. }
  1758. /* Disable RLB as we mostly have single-connection pools */
  1759. #if (OCI_MAJOR_VERSION > 10)
  1760. poolmode = OCI_SPC_NO_RLB | OCI_SPC_HOMOGENEOUS;
  1761. #else
  1762. poolmode = OCI_SPC_HOMOGENEOUS;
  1763. #endif
  1764. #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
  1765. /* {{{ Allocate auth handle for session pool */
  1766. PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **)&(spoolAuth), OCI_HTYPE_AUTHINFO, 0, NULL));
  1767. if (errstatus != OCI_SUCCESS) {
  1768. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1769. iserror = 1;
  1770. goto exit_create_spool;
  1771. }
  1772. /* }}} */
  1773. /* {{{ Set the edition attribute on the auth handle */
  1774. if (OCI_G(edition)) {
  1775. PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) OCI_G(edition), (ub4)(strlen(OCI_G(edition))), (ub4)OCI_ATTR_EDITION, OCI_G(err)));
  1776. if (errstatus != OCI_SUCCESS) {
  1777. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1778. iserror = 1;
  1779. goto exit_create_spool;
  1780. }
  1781. }
  1782. /* }}} */
  1783. /* {{{ Set the driver name attribute on the auth handle */
  1784. PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
  1785. if (errstatus != OCI_SUCCESS) {
  1786. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1787. iserror = 1;
  1788. goto exit_create_spool;
  1789. }
  1790. /* }}} */
  1791. /* {{{ Set the auth handle on the session pool */
  1792. PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, ((dvoid *) (session_pool->poolh),(ub4) OCI_HTYPE_SPOOL, (dvoid *) spoolAuth, (ub4)0, (ub4)OCI_ATTR_SPOOL_AUTH, OCI_G(err)));
  1793. if (errstatus != OCI_SUCCESS) {
  1794. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1795. iserror = 1;
  1796. goto exit_create_spool;
  1797. }
  1798. /* }}} */
  1799. #endif
  1800. /* Create the homogeneous session pool - We have different session pools for every different
  1801. * username, password, charset and dbname.
  1802. */
  1803. PHP_OCI_CALL_RETURN(errstatus, OCISessionPoolCreate,(session_pool->env, OCI_G(err), session_pool->poolh, (OraText **)&session_pool->poolname, &session_pool->poolname_len, (OraText *)dbname, (ub4)dbname_len, 0, UB4MAXVAL, 1,(OraText *)username, (ub4)username_len, (OraText *)password,(ub4)password_len, poolmode));
  1804. if (errstatus != OCI_SUCCESS) {
  1805. OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus);
  1806. iserror = 1;
  1807. }
  1808. exit_create_spool:
  1809. if (iserror && session_pool) {
  1810. php_oci_spool_close(session_pool);
  1811. session_pool = NULL;
  1812. }
  1813. if (spoolAuth) {
  1814. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) spoolAuth, (ub4) OCI_HTYPE_AUTHINFO));
  1815. }
  1816. #ifdef HAVE_OCI8_DTRACE
  1817. if (DTRACE_OCI8_SESSPOOL_CREATE_ENABLED()) {
  1818. DTRACE_OCI8_SESSPOOL_CREATE(session_pool);
  1819. }
  1820. #endif /* HAVE_OCI8_DTRACE */
  1821. return session_pool;
  1822. }
  1823. /* }}} */
  1824. /* {{{ php_oci_get_spool()
  1825. *
  1826. * Get Session pool for the given dbname and charsetid from the persistent list. Function called for
  1827. * non-persistent connections.
  1828. */
  1829. static php_oci_spool *php_oci_get_spool(char *username, int username_len, char *password, int password_len, char *dbname, int dbname_len, int charsetid)
  1830. {
  1831. smart_str spool_hashed_details = {0};
  1832. php_oci_spool *session_pool = NULL;
  1833. zend_resource *spool_out_le = NULL;
  1834. bool iserror = 0;
  1835. zval *spool_out_zv = NULL;
  1836. /* {{{ Create the spool hash key */
  1837. smart_str_appendl_ex(&spool_hashed_details, "oci8spool***", sizeof("oci8spool***") - 1, 0);
  1838. smart_str_appendl_ex(&spool_hashed_details, username, username_len, 0);
  1839. smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
  1840. /* Add edition attribute to the hash */
  1841. if (OCI_G(edition)){
  1842. smart_str_appendl_ex(&spool_hashed_details, OCI_G(edition), strlen(OCI_G(edition)), 0);
  1843. }
  1844. smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
  1845. if (password_len) {
  1846. zend_ulong password_hash;
  1847. password_hash = zend_hash_func(password, password_len);
  1848. smart_str_append_unsigned_ex(&spool_hashed_details, password_hash, 0);
  1849. }
  1850. smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
  1851. if (dbname_len) {
  1852. smart_str_appendl_ex(&spool_hashed_details, dbname, dbname_len, 0);
  1853. }
  1854. smart_str_appendl_ex(&spool_hashed_details, "**", sizeof("**") - 1, 0);
  1855. smart_str_append_unsigned_ex(&spool_hashed_details, charsetid, 0);
  1856. /* Session Pool Hash Key : oci8spool***username**edition**hashedpassword**dbname**charset */
  1857. smart_str_0(&spool_hashed_details);
  1858. zend_str_tolower(ZSTR_VAL(spool_hashed_details.s), ZSTR_LEN(spool_hashed_details.s));
  1859. /* }}} */
  1860. spool_out_zv = zend_hash_find(&EG(persistent_list), spool_hashed_details.s);
  1861. if (spool_out_zv != NULL) {
  1862. spool_out_le = Z_RES_P(spool_out_zv);
  1863. }
  1864. if (spool_out_le == NULL) {
  1865. session_pool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, spool_hashed_details.s, charsetid);
  1866. if (session_pool == NULL) {
  1867. iserror = 1;
  1868. goto exit_get_spool;
  1869. }
  1870. zend_register_persistent_resource_ex(session_pool->spool_hash_key, session_pool, le_psessionpool);
  1871. } else if (spool_out_le->type == le_psessionpool &&
  1872. ZSTR_LEN(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key) == ZSTR_LEN(spool_hashed_details.s) &&
  1873. memcmp(ZSTR_VAL(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key), ZSTR_VAL(spool_hashed_details.s), ZSTR_LEN(spool_hashed_details.s)) == 0) {
  1874. /* retrieve the cached session pool */
  1875. session_pool = (php_oci_spool *)(spool_out_le->ptr);
  1876. }
  1877. exit_get_spool:
  1878. smart_str_free(&spool_hashed_details);
  1879. if (iserror && session_pool) {
  1880. php_oci_spool_close(session_pool);
  1881. session_pool = NULL;
  1882. }
  1883. return session_pool;
  1884. }
  1885. /* }}} */
  1886. /* {{{ php_oci_create_env()
  1887. *
  1888. * Create the OCI environment choosing the correct function for the OCI version
  1889. */
  1890. static OCIEnv *php_oci_create_env(ub2 charsetid)
  1891. {
  1892. OCIEnv *retenv = NULL;
  1893. /* create an environment using the character set id */
  1894. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIEnvNlsCreate, (&retenv, OCI_G(events) ? PHP_OCI_INIT_MODE | OCI_EVENTS : PHP_OCI_INIT_MODE, 0, NULL, NULL, NULL, 0, NULL, charsetid, charsetid));
  1895. if (OCI_G(errcode) != OCI_SUCCESS) {
  1896. sb4 ora_error_code = 0;
  1897. text ora_msg_buf[PHP_OCI_ERRBUF_LEN]; /* Use traditional smaller size: non-PL/SQL errors should fit and it keeps the stack smaller */
  1898. #ifdef HAVE_OCI_INSTANT_CLIENT
  1899. php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that " PHP_OCI8_LIB_PATH_MSG " includes the directory with Oracle Instant Client libraries");
  1900. #else
  1901. php_error_docref(NULL, E_WARNING, "OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and " PHP_OCI8_LIB_PATH_MSG " are set and point to the right directories");
  1902. #endif
  1903. if (retenv
  1904. && OCIErrorGet(retenv, (ub4)1, NULL, &ora_error_code, ora_msg_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS
  1905. && *ora_msg_buf) {
  1906. php_error_docref(NULL, E_WARNING, "%s", ora_msg_buf);
  1907. }
  1908. return NULL;
  1909. }
  1910. return retenv;
  1911. }
  1912. /* }}} */
  1913. /* {{{ php_oci_old_create_session()
  1914. *
  1915. * This function is to be deprecated in future in favour of OCISessionGet which is used in
  1916. * php_oci_do_connect_ex
  1917. */
  1918. static int php_oci_old_create_session(php_oci_connection *connection, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode)
  1919. {
  1920. ub4 statement_cache_size = 0;
  1921. if (OCI_G(statement_cache_size) > 0) {
  1922. if (OCI_G(statement_cache_size) > SB4MAXVAL)
  1923. statement_cache_size = (ub4) SB4MAXVAL;
  1924. else
  1925. statement_cache_size = (ub4) OCI_G(statement_cache_size);
  1926. }
  1927. /* Create the OCI environment separate for each connection */
  1928. if (!(connection->env = php_oci_create_env(connection->charset))) {
  1929. return 1;
  1930. }
  1931. /* {{{ Allocate our server handle */
  1932. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->server), OCI_HTYPE_SERVER, 0, NULL));
  1933. if (OCI_G(errcode) != OCI_SUCCESS) {
  1934. php_oci_error(OCI_G(err), OCI_G(errcode));
  1935. return 1;
  1936. }
  1937. /* }}} */
  1938. /* {{{ Attach to the server */
  1939. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIServerAttach, (connection->server, OCI_G(err), (text *)dbname, dbname_len, (ub4) OCI_DEFAULT));
  1940. if (OCI_G(errcode) != OCI_SUCCESS) {
  1941. php_oci_error(OCI_G(err), OCI_G(errcode));
  1942. return 1;
  1943. }
  1944. /* }}} */
  1945. connection->is_attached = 1;
  1946. /* {{{ Allocate our session handle */
  1947. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->session), OCI_HTYPE_SESSION, 0, NULL));
  1948. if (OCI_G(errcode) != OCI_SUCCESS) {
  1949. php_oci_error(OCI_G(err), OCI_G(errcode));
  1950. return 1;
  1951. }
  1952. /* }}} */
  1953. /* {{{ Allocate our private error-handle */
  1954. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
  1955. if (OCI_G(errcode) != OCI_SUCCESS) {
  1956. php_oci_error(OCI_G(err), OCI_G(errcode));
  1957. return 1;
  1958. }
  1959. /* }}} */
  1960. /* {{{ Allocate our service-context */
  1961. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->svc), OCI_HTYPE_SVCCTX, 0, NULL));
  1962. if (OCI_G(errcode) != OCI_SUCCESS) {
  1963. php_oci_error(OCI_G(err), OCI_G(errcode));
  1964. return 1;
  1965. }
  1966. /* }}} */
  1967. /* {{{ Set the username */
  1968. if (username) {
  1969. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) username, (ub4) username_len, (ub4) OCI_ATTR_USERNAME, OCI_G(err)));
  1970. if (OCI_G(errcode) != OCI_SUCCESS) {
  1971. php_oci_error(OCI_G(err), OCI_G(errcode));
  1972. return 1;
  1973. }
  1974. }
  1975. /* }}} */
  1976. /* {{{ Set the password */
  1977. if (password) {
  1978. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) password, (ub4) password_len, (ub4) OCI_ATTR_PASSWORD, OCI_G(err)));
  1979. if (OCI_G(errcode) != OCI_SUCCESS) {
  1980. php_oci_error(OCI_G(err), OCI_G(errcode));
  1981. return 1;
  1982. }
  1983. }
  1984. /* }}} */
  1985. /* {{{ Set the edition attribute on the session handle */
  1986. #if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2)))
  1987. if (OCI_G(edition)) {
  1988. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(edition), (ub4) (strlen(OCI_G(edition))), (ub4) OCI_ATTR_EDITION, OCI_G(err)));
  1989. if (OCI_G(errcode) != OCI_SUCCESS) {
  1990. php_oci_error(OCI_G(err), OCI_G(errcode));
  1991. return 1;
  1992. }
  1993. }
  1994. #endif
  1995. /* }}} */
  1996. /* {{{ Set the driver name attribute on the session handle */
  1997. #if (OCI_MAJOR_VERSION >= 11)
  1998. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err)));
  1999. if (OCI_G(errcode) != OCI_SUCCESS) {
  2000. php_oci_error(OCI_G(err), OCI_G(errcode));
  2001. return 1;
  2002. }
  2003. #endif
  2004. /* }}} */
  2005. /* {{{ Set the server handle in the service handle */
  2006. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->server, 0, OCI_ATTR_SERVER, OCI_G(err)));
  2007. if (OCI_G(errcode) != OCI_SUCCESS) {
  2008. php_oci_error(OCI_G(err), OCI_G(errcode));
  2009. return 1;
  2010. }
  2011. /* }}} */
  2012. /* {{{ Set the authentication handle in the service handle */
  2013. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, (connection->svc, OCI_HTYPE_SVCCTX, connection->session, 0, OCI_ATTR_SESSION, OCI_G(err)));
  2014. if (OCI_G(errcode) != OCI_SUCCESS) {
  2015. php_oci_error(OCI_G(err), OCI_G(errcode));
  2016. return 1;
  2017. }
  2018. /* }}} */
  2019. if (new_password) {
  2020. /* {{{ Try to change password if new one was provided */
  2021. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIPasswordChange, (connection->svc, OCI_G(err), (text *)username, username_len, (text *)password, password_len, (text *)new_password, new_password_len, OCI_AUTH));
  2022. if (OCI_G(errcode) != OCI_SUCCESS) {
  2023. php_oci_error(OCI_G(err), OCI_G(errcode));
  2024. return 1;
  2025. }
  2026. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
  2027. if (OCI_G(errcode) != OCI_SUCCESS) {
  2028. php_oci_error(OCI_G(err), OCI_G(errcode));
  2029. return 1;
  2030. }
  2031. /* }}} */
  2032. } else {
  2033. /* {{{ start the session */
  2034. ub4 cred_type = OCI_CRED_RDBMS;
  2035. /* Extract the overloaded session_mode parameter into valid Oracle credential and session mode values */
  2036. if (session_mode & PHP_OCI_CRED_EXT) {
  2037. cred_type = OCI_CRED_EXT;
  2038. session_mode ^= PHP_OCI_CRED_EXT;
  2039. }
  2040. session_mode |= OCI_STMT_CACHE;
  2041. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCISessionBegin, (connection->svc, OCI_G(err), connection->session, (ub4) cred_type, (ub4) session_mode));
  2042. if (OCI_G(errcode) != OCI_SUCCESS) {
  2043. php_oci_error(OCI_G(err), OCI_G(errcode));
  2044. /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when
  2045. * user's password has expired, but is still usable.
  2046. */
  2047. if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
  2048. return 1;
  2049. }
  2050. }
  2051. /* }}} */
  2052. }
  2053. /* Brand new connection: Init and update the next_ping in the connection */
  2054. if (php_oci_ping_init(connection, OCI_G(err)) != OCI_SUCCESS) {
  2055. php_oci_error(OCI_G(err), OCI_G(errcode));
  2056. return 1;
  2057. }
  2058. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
  2059. if (OCI_G(errcode) != OCI_SUCCESS) {
  2060. php_oci_error(OCI_G(err), OCI_G(errcode));
  2061. return 1;
  2062. }
  2063. /* Successfully created session */
  2064. return 0;
  2065. }
  2066. /* }}} */
  2067. /* {{{ php_oci_create_session()
  2068. *
  2069. * Create session using client-side session pool - new norm
  2070. */
  2071. static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode)
  2072. {
  2073. php_oci_spool *actual_spool = NULL;
  2074. #if (OCI_MAJOR_VERSION > 10)
  2075. ub4 purity = -2; /* Illegal value to initialize */
  2076. #endif
  2077. time_t timestamp = time(NULL);
  2078. ub4 statement_cache_size = 0;
  2079. if (OCI_G(statement_cache_size) > 0) {
  2080. if (OCI_G(statement_cache_size) > SB4MAXVAL)
  2081. statement_cache_size = (ub4) SB4MAXVAL;
  2082. else
  2083. statement_cache_size = (ub4) OCI_G(statement_cache_size);
  2084. }
  2085. /* Persistent connections have private session pools */
  2086. if (connection->is_persistent && !connection->private_spool &&
  2087. !(connection->private_spool = php_oci_create_spool(username, username_len, password, password_len, dbname, dbname_len, NULL, connection->charset))) {
  2088. return 1;
  2089. }
  2090. actual_spool = (connection->is_persistent) ? (connection->private_spool) : (session_pool);
  2091. connection->env = actual_spool->env;
  2092. /* Do this upfront so that connection close on an error would know that this is a session pool
  2093. * connection. Failure to do this would result in crashes in error scenarios
  2094. */
  2095. if (!connection->using_spool) {
  2096. connection->using_spool = 1;
  2097. }
  2098. #ifdef HAVE_OCI8_DTRACE
  2099. if (DTRACE_OCI8_SESSPOOL_TYPE_ENABLED()) {
  2100. DTRACE_OCI8_SESSPOOL_TYPE(session_pool ? 1 : 0, session_pool ? session_pool : connection->private_spool);
  2101. }
  2102. #endif /* HAVE_OCI8_DTRACE */
  2103. /* The passed in "connection" can be a cached stub from plist or freshly created. In the former
  2104. * case, we do not have to allocate any handles
  2105. */
  2106. if (!connection->err) {
  2107. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->err), OCI_HTYPE_ERROR, 0, NULL));
  2108. if (OCI_G(errcode) != OCI_SUCCESS) {
  2109. php_oci_error(OCI_G(err), OCI_G(errcode));
  2110. return 1;
  2111. }
  2112. }
  2113. /* {{{ Allocate and initialize the connection-private authinfo handle if not allocated yet */
  2114. if (!connection->authinfo) {
  2115. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIHandleAlloc, (connection->env, (dvoid **)&(connection->authinfo), OCI_HTYPE_AUTHINFO, 0, NULL));
  2116. if (OCI_G(errcode) != OCI_SUCCESS) {
  2117. php_oci_error(OCI_G(err), OCI_G(errcode));
  2118. return 1;
  2119. }
  2120. /* Set the Connection class and purity if OCI client version >= 11g */
  2121. #if (OCI_MAJOR_VERSION > 10)
  2122. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(connection_class), (ub4)(strlen(OCI_G(connection_class))), (ub4)OCI_ATTR_CONNECTION_CLASS, OCI_G(err)));
  2123. if (OCI_G(errcode) != OCI_SUCCESS) {
  2124. php_oci_error(OCI_G(err), OCI_G(errcode));
  2125. return 1;
  2126. }
  2127. if (connection->is_persistent)
  2128. purity = OCI_ATTR_PURITY_SELF;
  2129. else
  2130. purity = OCI_ATTR_PURITY_NEW;
  2131. PHP_OCI_CALL_RETURN(OCI_G(errcode),OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_AUTHINFO, (dvoid *) &purity, (ub4)0, (ub4)OCI_ATTR_PURITY, OCI_G(err)));
  2132. if (OCI_G(errcode) != OCI_SUCCESS) {
  2133. php_oci_error(OCI_G(err), OCI_G(errcode));
  2134. return 1;
  2135. }
  2136. #endif
  2137. }
  2138. /* }}} */
  2139. /* {{{ Debug statements */
  2140. #ifdef HAVE_OCI8_DTRACE
  2141. if (DTRACE_OCI8_SESSPOOL_STATS_ENABLED()) {
  2142. ub4 numfree = 0, numbusy = 0, numopen = 0;
  2143. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numopen, (ub4 *)0, OCI_ATTR_SPOOL_OPEN_COUNT, OCI_G(err)));
  2144. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)actual_spool->poolh, OCI_HTYPE_SPOOL, (dvoid *)&numbusy, (ub4 *)0, OCI_ATTR_SPOOL_BUSY_COUNT, OCI_G(err)));
  2145. numfree = numopen - numbusy; /* number of free connections in the pool */
  2146. DTRACE_OCI8_SESSPOOL_STATS(numfree, numbusy, numopen);
  2147. }
  2148. #endif /* HAVE_OCI8_DTRACE */
  2149. /* }}} */
  2150. /* Ping loop: Ping and loop till we get a good connection. When a database instance goes
  2151. * down, it can leave several bad connections that need to be flushed out before getting a
  2152. * good one. In non-RAC, we always get a brand new connection at the end of the loop and in
  2153. * RAC, we can get a good connection from a different instance before flushing out all bad
  2154. * ones. We do not need to ping brand new connections.
  2155. */
  2156. do {
  2157. /* Continue to use the global error handle as the connection is closed when an error occurs */
  2158. PHP_OCI_CALL_RETURN(OCI_G(errcode),OCISessionGet, (connection->env, OCI_G(err), &(connection->svc), (OCIAuthInfo *)connection->authinfo, (OraText *)actual_spool->poolname, (ub4)actual_spool->poolname_len, NULL, 0, NULL, NULL, NULL, OCI_SESSGET_SPOOL));
  2159. if (OCI_G(errcode) != OCI_SUCCESS) {
  2160. php_oci_error(OCI_G(err), OCI_G(errcode));
  2161. /* Session creation returns OCI_SUCCESS_WITH_INFO when user's password has expired, but
  2162. * is still usable.
  2163. */
  2164. if (OCI_G(errcode) != OCI_SUCCESS_WITH_INFO) {
  2165. return 1;
  2166. }
  2167. }
  2168. /* {{{ Populate the session and server fields of the connection */
  2169. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->server), (ub4 *)0, OCI_ATTR_SERVER, OCI_G(err)));
  2170. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrGet, ((dvoid *)connection->svc, OCI_HTYPE_SVCCTX, (dvoid *)&(connection->session), (ub4 *)0, OCI_ATTR_SESSION, OCI_G(err)));
  2171. /* }}} */
  2172. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, OCI_G(err), (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&(connection->next_pingp)));
  2173. if (OCI_G(errcode) != OCI_SUCCESS) {
  2174. php_oci_error(OCI_G(err), OCI_G(errcode));
  2175. return 1;
  2176. }
  2177. if (!(connection->next_pingp)){
  2178. /* This is a brand new connection, we need not ping, but have to initialize ping */
  2179. if (php_oci_ping_init(connection, OCI_G(err)) != OCI_SUCCESS) {
  2180. php_oci_error(OCI_G(err), OCI_G(errcode));
  2181. return 1;
  2182. }
  2183. } else if ((*(connection->next_pingp) > 0) && (timestamp >= *(connection->next_pingp))) {
  2184. if (php_oci_connection_ping(connection)) {
  2185. /* Got a good connection - update next_ping and get out of ping loop */
  2186. *(connection->next_pingp) = timestamp + OCI_G(ping_interval);
  2187. } else {
  2188. /* Bad connection - remove from pool */
  2189. PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL,0, (ub4) OCI_SESSRLS_DROPSESS));
  2190. connection->svc = NULL;
  2191. connection->server = NULL;
  2192. connection->session = NULL;
  2193. }
  2194. } /* If ping applicable */
  2195. } while (!(connection->svc));
  2196. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->svc, (ub4) OCI_HTYPE_SVCCTX, (ub4 *) &statement_cache_size, 0, (ub4) OCI_ATTR_STMTCACHESIZE, OCI_G(err)));
  2197. if (OCI_G(errcode) != OCI_SUCCESS) {
  2198. php_oci_error(OCI_G(err), OCI_G(errcode));
  2199. return 1;
  2200. }
  2201. /* Session is now taken from the session pool and attached and open */
  2202. connection->is_stub = 0;
  2203. connection->is_attached = connection->is_open = 1;
  2204. return 0;
  2205. }
  2206. /* }}} */
  2207. /* {{{ php_oci_spool_list_dtor()
  2208. *
  2209. * Session pool destructor function
  2210. */
  2211. static void php_oci_spool_list_dtor(zend_resource *entry)
  2212. {
  2213. php_oci_spool *session_pool = (php_oci_spool *)entry->ptr;
  2214. if (session_pool) {
  2215. php_oci_spool_close(session_pool);
  2216. }
  2217. return;
  2218. }
  2219. /* }}} */
  2220. /* {{{ php_oci_spool_close()
  2221. *
  2222. * Destroys the OCI Session Pool
  2223. */
  2224. static void php_oci_spool_close(php_oci_spool *session_pool)
  2225. {
  2226. if (session_pool->poolname_len) {
  2227. PHP_OCI_CALL(OCISessionPoolDestroy, ((dvoid *) session_pool->poolh,
  2228. (dvoid *) session_pool->err, OCI_SPD_FORCE));
  2229. }
  2230. if (session_pool->poolh) {
  2231. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->poolh, OCI_HTYPE_SPOOL));
  2232. }
  2233. if (session_pool->err) {
  2234. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->err, OCI_HTYPE_ERROR));
  2235. }
  2236. if (session_pool->env) {
  2237. PHP_OCI_CALL(OCIHandleFree, ((dvoid *) session_pool->env, OCI_HTYPE_ENV));
  2238. }
  2239. if (session_pool->spool_hash_key) {
  2240. free(session_pool->spool_hash_key);
  2241. }
  2242. free(session_pool);
  2243. }
  2244. /* }}} */
  2245. /* {{{ php_oci_ping_init()
  2246. *
  2247. * Initializes the next_ping time as a context value in the connection. We now use
  2248. * OCIContext{Get,Set}Value to store the next_ping because we need to support ping for
  2249. * non-persistent DRCP connections
  2250. */
  2251. static sword php_oci_ping_init(php_oci_connection *connection, OCIError *errh)
  2252. {
  2253. time_t *next_pingp = NULL;
  2254. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextGetValue, (connection->session, errh, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), (void **)&next_pingp));
  2255. if (OCI_G(errcode) != OCI_SUCCESS) {
  2256. return OCI_G(errcode);
  2257. }
  2258. /* This must be a brand-new connection. Allocate memory for the ping */
  2259. if (!next_pingp) {
  2260. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIMemoryAlloc, (connection->session, errh, (void **)&next_pingp, OCI_DURATION_SESSION, sizeof(time_t), OCI_MEMORY_CLEARED));
  2261. if (OCI_G(errcode) != OCI_SUCCESS) {
  2262. return OCI_G(errcode);
  2263. }
  2264. }
  2265. if (OCI_G(ping_interval) >= 0) {
  2266. time_t timestamp = time(NULL);
  2267. *next_pingp = timestamp + OCI_G(ping_interval);
  2268. } else {
  2269. *next_pingp = 0;
  2270. }
  2271. /* Set the new ping value into the connection */
  2272. PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIContextSetValue, (connection->session, errh, OCI_DURATION_SESSION, (ub1 *)"NEXT_PING", (ub1)sizeof("NEXT_PING"), next_pingp));
  2273. if (OCI_G(errcode) != OCI_SUCCESS) {
  2274. OCIMemoryFree(connection->session, errh, next_pingp);
  2275. return OCI_G(errcode);
  2276. }
  2277. /* Cache the pointer so we do not have to do OCIContextGetValue repeatedly */
  2278. connection->next_pingp = next_pingp;
  2279. return OCI_SUCCESS;
  2280. }
  2281. /* }}} */
  2282. /* {{{ php_oci_dtrace_check_connection()
  2283. *
  2284. * DTrace output for connections that may have become invalid and marked for reopening
  2285. */
  2286. void php_oci_dtrace_check_connection(php_oci_connection *connection, sb4 errcode, ub4 serverStatus)
  2287. {
  2288. #ifdef HAVE_OCI8_DTRACE
  2289. if (DTRACE_OCI8_CHECK_CONNECTION_ENABLED()) {
  2290. DTRACE_OCI8_CHECK_CONNECTION(connection, connection->client_id, connection->is_open ? 1 : 0, (long)errcode, (unsigned long)serverStatus);
  2291. }
  2292. #endif /* HAVE_OCI8_DTRACE */
  2293. }
  2294. /* }}} */
  2295. #endif /* HAVE_OCI8 */