mysqlnd_connection.c 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2006-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andrey Hristov <andrey@php.net> |
  16. | Ulf Wendel <uw@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. #include "php.h"
  20. #include "mysqlnd.h"
  21. #include "mysqlnd_connection.h"
  22. #include "mysqlnd_vio.h"
  23. #include "mysqlnd_protocol_frame_codec.h"
  24. #include "mysqlnd_auth.h"
  25. #include "mysqlnd_wireprotocol.h"
  26. #include "mysqlnd_priv.h"
  27. #include "mysqlnd_result.h"
  28. #include "mysqlnd_statistics.h"
  29. #include "mysqlnd_charset.h"
  30. #include "mysqlnd_debug.h"
  31. #include "mysqlnd_ext_plugin.h"
  32. #include "zend_smart_str.h"
  33. extern MYSQLND_CHARSET *mysqlnd_charsets;
  34. PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
  35. PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
  36. PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
  37. PHPAPI MYSQLND_STATS * mysqlnd_global_stats = NULL;
  38. /* {{{ mysqlnd_upsert_status::reset */
  39. void
  40. MYSQLND_METHOD(mysqlnd_upsert_status, reset)(MYSQLND_UPSERT_STATUS * const upsert_status)
  41. {
  42. upsert_status->warning_count = 0;
  43. upsert_status->server_status = 0;
  44. upsert_status->affected_rows = 0;
  45. upsert_status->last_insert_id = 0;
  46. }
  47. /* }}} */
  48. /* {{{ mysqlnd_upsert_status::set_affected_rows_to_error */
  49. void
  50. MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * upsert_status)
  51. {
  52. upsert_status->affected_rows = (uint64_t) ~0;
  53. }
  54. /* }}} */
  55. MYSQLND_CLASS_METHODS_START(mysqlnd_upsert_status)
  56. MYSQLND_METHOD(mysqlnd_upsert_status, reset),
  57. MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error),
  58. MYSQLND_CLASS_METHODS_END;
  59. /* {{{ mysqlnd_upsert_status_init */
  60. void
  61. mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status)
  62. {
  63. upsert_status->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_upsert_status);
  64. upsert_status->m->reset(upsert_status);
  65. }
  66. /* }}} */
  67. /* {{{ mysqlnd_error_list_pdtor */
  68. static void
  69. mysqlnd_error_list_pdtor(void * pDest)
  70. {
  71. MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
  72. DBG_ENTER("mysqlnd_error_list_pdtor");
  73. if (element->error) {
  74. mnd_pefree(element->error, TRUE);
  75. }
  76. DBG_VOID_RETURN;
  77. }
  78. /* }}} */
  79. /* {{{ mysqlnd_error_info::reset */
  80. static void
  81. MYSQLND_METHOD(mysqlnd_error_info, reset)(MYSQLND_ERROR_INFO * const info)
  82. {
  83. DBG_ENTER("mysqlnd_error_info::reset");
  84. info->error_no = 0;
  85. info->error[0] = '\0';
  86. memset(&info->sqlstate, 0, sizeof(info->sqlstate));
  87. zend_llist_clean(&info->error_list);
  88. DBG_VOID_RETURN;
  89. }
  90. /* }}} */
  91. /* {{{ mysqlnd_error_info::set_client_error */
  92. static void
  93. MYSQLND_METHOD(mysqlnd_error_info, set_client_error)(MYSQLND_ERROR_INFO * const info,
  94. const unsigned int err_no,
  95. const char * const sqlstate,
  96. const char * const error)
  97. {
  98. DBG_ENTER("mysqlnd_error_info::set_client_error");
  99. if (err_no) {
  100. MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0};
  101. info->error_no = err_no;
  102. strlcpy(info->sqlstate, sqlstate, sizeof(info->sqlstate));
  103. strlcpy(info->error, error, sizeof(info->error));
  104. error_for_the_list.error_no = err_no;
  105. strlcpy(error_for_the_list.sqlstate, sqlstate, sizeof(error_for_the_list.sqlstate));
  106. error_for_the_list.error = mnd_pestrdup(error, TRUE);
  107. if (error_for_the_list.error) {
  108. DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error);
  109. zend_llist_add_element(&info->error_list, &error_for_the_list);
  110. }
  111. } else {
  112. info->m->reset(info);
  113. }
  114. DBG_VOID_RETURN;
  115. }
  116. /* }}} */
  117. MYSQLND_CLASS_METHODS_START(mysqlnd_error_info)
  118. MYSQLND_METHOD(mysqlnd_error_info, reset),
  119. MYSQLND_METHOD(mysqlnd_error_info, set_client_error),
  120. MYSQLND_CLASS_METHODS_END;
  121. /* {{{ mysqlnd_error_info_init */
  122. PHPAPI enum_func_status
  123. mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent)
  124. {
  125. DBG_ENTER("mysqlnd_error_info_init");
  126. info->m = mysqlnd_error_info_get_methods();
  127. info->m->reset(info);
  128. zend_llist_init(&info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, persistent);
  129. info->persistent = persistent;
  130. DBG_RETURN(PASS);
  131. }
  132. /* }}} */
  133. /* {{{ mysqlnd_error_info_free_contents */
  134. PHPAPI void
  135. mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info)
  136. {
  137. DBG_ENTER("mysqlnd_error_info_free_contents");
  138. info->m->reset(info);
  139. DBG_VOID_RETURN;
  140. }
  141. /* }}} */
  142. /* {{{ mysqlnd_connection_state::get */
  143. static enum mysqlnd_connection_state
  144. MYSQLND_METHOD(mysqlnd_connection_state, get)(const struct st_mysqlnd_connection_state * const state_struct)
  145. {
  146. DBG_ENTER("mysqlnd_connection_state::get");
  147. DBG_INF_FMT("State=%u", state_struct->state);
  148. DBG_RETURN(state_struct->state);
  149. }
  150. /* }}} */
  151. /* {{{ mysqlnd_connection_state::set */
  152. static void
  153. MYSQLND_METHOD(mysqlnd_connection_state, set)(struct st_mysqlnd_connection_state * const state_struct, const enum mysqlnd_connection_state state)
  154. {
  155. DBG_ENTER("mysqlnd_connection_state::set");
  156. DBG_INF_FMT("New state=%u", state);
  157. state_struct->state = state;
  158. DBG_VOID_RETURN;
  159. }
  160. /* }}} */
  161. MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
  162. MYSQLND_METHOD(mysqlnd_connection_state, get),
  163. MYSQLND_METHOD(mysqlnd_connection_state, set),
  164. MYSQLND_CLASS_METHODS_END;
  165. /* {{{ mysqlnd_connection_state_init */
  166. PHPAPI void
  167. mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)
  168. {
  169. DBG_ENTER("mysqlnd_connection_state_init");
  170. state->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_connection_state);
  171. state->state = CONN_ALLOCED;
  172. DBG_VOID_RETURN;
  173. }
  174. /* }}} */
  175. /* {{{ mysqlnd_conn_data::free_options */
  176. static void
  177. MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn)
  178. {
  179. zend_bool pers = conn->persistent;
  180. if (conn->options->charset_name) {
  181. mnd_pefree(conn->options->charset_name, pers);
  182. conn->options->charset_name = NULL;
  183. }
  184. if (conn->options->auth_protocol) {
  185. mnd_pefree(conn->options->auth_protocol, pers);
  186. conn->options->auth_protocol = NULL;
  187. }
  188. if (conn->options->num_commands) {
  189. unsigned int i;
  190. for (i = 0; i < conn->options->num_commands; i++) {
  191. /* allocated with pestrdup */
  192. mnd_pefree(conn->options->init_commands[i], pers);
  193. }
  194. mnd_pefree(conn->options->init_commands, pers);
  195. conn->options->init_commands = NULL;
  196. }
  197. if (conn->options->cfg_file) {
  198. mnd_pefree(conn->options->cfg_file, pers);
  199. conn->options->cfg_file = NULL;
  200. }
  201. if (conn->options->cfg_section) {
  202. mnd_pefree(conn->options->cfg_section, pers);
  203. conn->options->cfg_section = NULL;
  204. }
  205. if (conn->options->connect_attr) {
  206. zend_hash_destroy(conn->options->connect_attr);
  207. mnd_pefree(conn->options->connect_attr, pers);
  208. conn->options->connect_attr = NULL;
  209. }
  210. }
  211. /* }}} */
  212. /* {{{ mysqlnd_conn_data::free_contents */
  213. static void
  214. MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
  215. {
  216. zend_bool pers = conn->persistent;
  217. DBG_ENTER("mysqlnd_conn_data::free_contents");
  218. if (conn->current_result) {
  219. conn->current_result->m.free_result(conn->current_result, TRUE);
  220. conn->current_result = NULL;
  221. }
  222. if (conn->protocol_frame_codec) {
  223. conn->protocol_frame_codec->data->m.free_contents(conn->protocol_frame_codec);
  224. }
  225. if (conn->vio) {
  226. conn->vio->data->m.free_contents(conn->vio);
  227. }
  228. DBG_INF("Freeing memory of members");
  229. if (conn->hostname.s) {
  230. mnd_pefree(conn->hostname.s, pers);
  231. conn->hostname.s = NULL;
  232. }
  233. if (conn->username.s) {
  234. mnd_pefree(conn->username.s, pers);
  235. conn->username.s = NULL;
  236. }
  237. if (conn->password.s) {
  238. mnd_pefree(conn->password.s, pers);
  239. conn->password.s = NULL;
  240. }
  241. if (conn->connect_or_select_db.s) {
  242. mnd_pefree(conn->connect_or_select_db.s, pers);
  243. conn->connect_or_select_db.s = NULL;
  244. }
  245. if (conn->unix_socket.s) {
  246. mnd_pefree(conn->unix_socket.s, pers);
  247. conn->unix_socket.s = NULL;
  248. }
  249. DBG_INF_FMT("scheme=%s", conn->scheme.s);
  250. if (conn->scheme.s) {
  251. mnd_pefree(conn->scheme.s, pers);
  252. conn->scheme.s = NULL;
  253. }
  254. if (conn->server_version) {
  255. mnd_pefree(conn->server_version, pers);
  256. conn->server_version = NULL;
  257. }
  258. if (conn->host_info) {
  259. mnd_pefree(conn->host_info, pers);
  260. conn->host_info = NULL;
  261. }
  262. if (conn->authentication_plugin_data.s) {
  263. mnd_pefree(conn->authentication_plugin_data.s, pers);
  264. conn->authentication_plugin_data.s = NULL;
  265. }
  266. if (conn->last_message.s) {
  267. mnd_efree(conn->last_message.s);
  268. conn->last_message.s = NULL;
  269. }
  270. conn->charset = NULL;
  271. conn->greet_charset = NULL;
  272. DBG_VOID_RETURN;
  273. }
  274. /* }}} */
  275. /* {{{ mysqlnd_conn_data::dtor */
  276. static void
  277. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn)
  278. {
  279. DBG_ENTER("mysqlnd_conn_data::dtor");
  280. DBG_INF_FMT("conn=%llu", conn->thread_id);
  281. conn->m->free_contents(conn);
  282. conn->m->free_options(conn);
  283. if (conn->error_info) {
  284. mysqlnd_error_info_free_contents(conn->error_info);
  285. conn->error_info = NULL;
  286. }
  287. if (conn->protocol_frame_codec) {
  288. mysqlnd_pfc_free(conn->protocol_frame_codec, conn->stats, conn->error_info);
  289. conn->protocol_frame_codec = NULL;
  290. }
  291. if (conn->vio) {
  292. mysqlnd_vio_free(conn->vio, conn->stats, conn->error_info);
  293. conn->vio = NULL;
  294. }
  295. if (conn->payload_decoder_factory) {
  296. mysqlnd_protocol_payload_decoder_factory_free(conn->payload_decoder_factory);
  297. conn->payload_decoder_factory = NULL;
  298. }
  299. if (conn->stats) {
  300. mysqlnd_stats_end(conn->stats, conn->persistent);
  301. }
  302. mnd_pefree(conn, conn->persistent);
  303. DBG_VOID_RETURN;
  304. }
  305. /* }}} */
  306. /* {{{ mysqlnd_conn_data::set_server_option */
  307. static enum_func_status
  308. MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option)
  309. {
  310. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_server_option);
  311. enum_func_status ret = FAIL;
  312. DBG_ENTER("mysqlnd_conn_data::set_server_option");
  313. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  314. ret = conn->run_command(COM_SET_OPTION, conn, option);
  315. conn->m->local_tx_end(conn, this_func, ret);
  316. }
  317. DBG_RETURN(ret);
  318. }
  319. /* }}} */
  320. /* {{{ mysqlnd_conn_data::restart_psession */
  321. static enum_func_status
  322. MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn)
  323. {
  324. DBG_ENTER("mysqlnd_conn_data::restart_psession");
  325. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
  326. conn->current_result = NULL;
  327. conn->last_message.s = NULL;
  328. DBG_RETURN(PASS);
  329. }
  330. /* }}} */
  331. /* {{{ mysqlnd_conn_data::end_psession */
  332. static enum_func_status
  333. MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn)
  334. {
  335. DBG_ENTER("mysqlnd_conn_data::end_psession");
  336. /* Free here what should not be seen by the next script */
  337. if (conn->current_result) {
  338. conn->current_result->m.free_result(conn->current_result, TRUE);
  339. conn->current_result = NULL;
  340. }
  341. if (conn->last_message.s) {
  342. mnd_efree(conn->last_message.s);
  343. conn->last_message.s = NULL;
  344. }
  345. conn->error_info = &conn->error_info_impl;
  346. DBG_RETURN(PASS);
  347. }
  348. /* }}} */
  349. /* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
  350. static struct st_mysqlnd_authentication_plugin *
  351. MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol)
  352. {
  353. struct st_mysqlnd_authentication_plugin * auth_plugin;
  354. char * plugin_name = NULL;
  355. DBG_ENTER("mysqlnd_conn_data::fetch_auth_plugin_by_name");
  356. mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
  357. DBG_INF_FMT("looking for %s auth plugin", plugin_name);
  358. auth_plugin = mysqlnd_plugin_find(plugin_name);
  359. mnd_sprintf_free(plugin_name);
  360. DBG_RETURN(auth_plugin);
  361. }
  362. /* }}} */
  363. /* {{{ mysqlnd_conn_data::execute_init_commands */
  364. static enum_func_status
  365. MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn)
  366. {
  367. enum_func_status ret = PASS;
  368. DBG_ENTER("mysqlnd_conn_data::execute_init_commands");
  369. if (conn->options->init_commands) {
  370. unsigned int current_command = 0;
  371. for (; current_command < conn->options->num_commands; ++current_command) {
  372. const char * const command = conn->options->init_commands[current_command];
  373. if (command) {
  374. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
  375. if (PASS != conn->m->query(conn, command, strlen(command))) {
  376. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
  377. ret = FAIL;
  378. break;
  379. }
  380. if (conn->last_query_type == QUERY_SELECT) {
  381. MYSQLND_RES * result = conn->m->use_result(conn, 0);
  382. if (result) {
  383. result->m.free_result(result, TRUE);
  384. }
  385. }
  386. }
  387. }
  388. }
  389. DBG_RETURN(ret);
  390. }
  391. /* }}} */
  392. /* {{{ mysqlnd_conn_data::get_updated_connect_flags */
  393. static unsigned int
  394. MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags)
  395. {
  396. #ifdef MYSQLND_COMPRESSION_ENABLED
  397. MYSQLND_PFC * pfc = conn->protocol_frame_codec;
  398. #endif
  399. MYSQLND_VIO * vio = conn->vio;
  400. DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
  401. /* allow CLIENT_LOCAL_FILES capability, although extensions basing on mysqlnd
  402. shouldn't allow 'load data local infile' by default due to security issues */
  403. mysql_flags |= MYSQLND_CAPABILITIES;
  404. mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */
  405. #ifndef MYSQLND_COMPRESSION_ENABLED
  406. if (mysql_flags & CLIENT_COMPRESS) {
  407. mysql_flags &= ~CLIENT_COMPRESS;
  408. }
  409. #else
  410. if (pfc && pfc->data->flags & MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION) {
  411. mysql_flags |= CLIENT_COMPRESS;
  412. }
  413. #endif
  414. #ifndef MYSQLND_SSL_SUPPORTED
  415. if (mysql_flags & CLIENT_SSL) {
  416. mysql_flags &= ~CLIENT_SSL;
  417. }
  418. #else
  419. if (vio && (vio->data->options.ssl_key ||
  420. vio->data->options.ssl_cert ||
  421. vio->data->options.ssl_ca ||
  422. vio->data->options.ssl_capath ||
  423. vio->data->options.ssl_cipher))
  424. {
  425. mysql_flags |= CLIENT_SSL;
  426. }
  427. #endif
  428. if (conn->options->connect_attr && zend_hash_num_elements(conn->options->connect_attr)) {
  429. mysql_flags |= CLIENT_CONNECT_ATTRS;
  430. }
  431. DBG_RETURN(mysql_flags);
  432. }
  433. /* }}} */
  434. /* {{{ mysqlnd_conn_data::connect_handshake */
  435. static enum_func_status
  436. MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
  437. const MYSQLND_CSTRING * const scheme,
  438. const MYSQLND_CSTRING * const username,
  439. const MYSQLND_CSTRING * const password,
  440. const MYSQLND_CSTRING * const database,
  441. const unsigned int mysql_flags)
  442. {
  443. enum_func_status ret = FAIL;
  444. DBG_ENTER("mysqlnd_conn_data::connect_handshake");
  445. if (PASS == conn->vio->data->m.connect(conn->vio, *scheme, conn->persistent, conn->stats, conn->error_info) &&
  446. PASS == conn->protocol_frame_codec->data->m.reset(conn->protocol_frame_codec, conn->stats, conn->error_info))
  447. {
  448. size_t client_flags = mysql_flags;
  449. ret = conn->run_command(COM_HANDSHAKE, conn, username, password, database, client_flags);
  450. }
  451. DBG_RETURN(ret);
  452. }
  453. /* }}} */
  454. /* {{{ mysqlnd_conn_data::get_scheme */
  455. static MYSQLND_STRING
  456. MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe)
  457. {
  458. MYSQLND_STRING transport;
  459. DBG_ENTER("mysqlnd_conn_data::get_scheme");
  460. #ifndef PHP_WIN32
  461. if (hostname.l == sizeof("localhost") - 1 && !strncasecmp(hostname.s, "localhost", hostname.l)) {
  462. DBG_INF_FMT("socket=%s", socket_or_pipe->s? socket_or_pipe->s:"n/a");
  463. if (!socket_or_pipe->s) {
  464. socket_or_pipe->s = "/tmp/mysql.sock";
  465. socket_or_pipe->l = strlen(socket_or_pipe->s);
  466. }
  467. transport.l = mnd_sprintf(&transport.s, 0, "unix://%s", socket_or_pipe->s);
  468. *unix_socket = TRUE;
  469. #else
  470. if (hostname.l == sizeof(".") - 1 && hostname.s[0] == '.') {
  471. /* named pipe in socket */
  472. if (!socket_or_pipe->s) {
  473. socket_or_pipe->s = "\\\\.\\pipe\\MySQL";
  474. socket_or_pipe->l = strlen(socket_or_pipe->s);
  475. }
  476. transport.l = mnd_sprintf(&transport.s, 0, "pipe://%s", socket_or_pipe->s);
  477. *named_pipe = TRUE;
  478. #endif
  479. } else {
  480. if (!port) {
  481. port = 3306;
  482. }
  483. transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port);
  484. }
  485. DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM");
  486. DBG_RETURN(transport);
  487. }
  488. /* }}} */
  489. /* {{{ mysqlnd_conn_data::connect */
  490. static enum_func_status
  491. MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
  492. MYSQLND_CSTRING hostname,
  493. MYSQLND_CSTRING username,
  494. MYSQLND_CSTRING password,
  495. MYSQLND_CSTRING database,
  496. unsigned int port,
  497. MYSQLND_CSTRING socket_or_pipe,
  498. unsigned int mysql_flags
  499. )
  500. {
  501. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
  502. zend_bool unix_socket = FALSE;
  503. zend_bool named_pipe = FALSE;
  504. zend_bool reconnect = FALSE;
  505. zend_bool saved_compression = FALSE;
  506. zend_bool local_tx_started = FALSE;
  507. MYSQLND_PFC * pfc = conn->protocol_frame_codec;
  508. MYSQLND_STRING transport = { NULL, 0 };
  509. DBG_ENTER("mysqlnd_conn_data::connect");
  510. DBG_INF_FMT("conn=%p", conn);
  511. if (PASS != conn->m->local_tx_start(conn, this_func)) {
  512. goto err;
  513. }
  514. local_tx_started = TRUE;
  515. SET_EMPTY_ERROR(conn->error_info);
  516. UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
  517. DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
  518. hostname.s?hostname.s:"", username.s?username.s:"", database.s?database.s:"", port, mysql_flags,
  519. conn? conn->persistent:0, conn? (int)GET_CONNECTION_STATE(&conn->state):-1);
  520. if (GET_CONNECTION_STATE(&conn->state) > CONN_ALLOCED) {
  521. DBG_INF("Connecting on a connected handle.");
  522. if (GET_CONNECTION_STATE(&conn->state) < CONN_QUIT_SENT) {
  523. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
  524. reconnect = TRUE;
  525. conn->m->send_close(conn);
  526. }
  527. conn->m->free_contents(conn);
  528. /* Now reconnect using the same handle */
  529. if (pfc->data->compressed) {
  530. /*
  531. we need to save the state. As we will re-connect, pfc->compressed should be off, or
  532. we will look for a compression header as part of the greet message, but there will
  533. be none.
  534. */
  535. saved_compression = TRUE;
  536. pfc->data->compressed = FALSE;
  537. }
  538. if (pfc->data->ssl) {
  539. pfc->data->ssl = FALSE;
  540. }
  541. } else {
  542. unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
  543. conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size);
  544. }
  545. if (!hostname.s || !hostname.s[0]) {
  546. hostname.s = "localhost";
  547. hostname.l = strlen(hostname.s);
  548. }
  549. if (!username.s) {
  550. DBG_INF_FMT("no user given, using empty string");
  551. username.s = "";
  552. username.l = 0;
  553. }
  554. if (!password.s) {
  555. DBG_INF_FMT("no password given, using empty string");
  556. password.s = "";
  557. password.l = 0;
  558. }
  559. if (!database.s || !database.s[0]) {
  560. DBG_INF_FMT("no db given, using empty string");
  561. database.s = "";
  562. database.l = 0;
  563. } else {
  564. mysql_flags |= CLIENT_CONNECT_WITH_DB;
  565. }
  566. transport = conn->m->get_scheme(conn, hostname, &socket_or_pipe, port, &unix_socket, &named_pipe);
  567. mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags);
  568. {
  569. const MYSQLND_CSTRING scheme = { transport.s, transport.l };
  570. if (FAIL == conn->m->connect_handshake(conn, &scheme, &username, &password, &database, mysql_flags)) {
  571. goto err;
  572. }
  573. }
  574. {
  575. SET_CONNECTION_STATE(&conn->state, CONN_READY);
  576. if (saved_compression) {
  577. pfc->data->compressed = TRUE;
  578. }
  579. /*
  580. If a connect on a existing handle is performed and mysql_flags is
  581. passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
  582. which we set based on saved_compression.
  583. */
  584. pfc->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
  585. conn->scheme.s = mnd_pestrndup(transport.s, transport.l, conn->persistent);
  586. conn->scheme.l = transport.l;
  587. if (transport.s) {
  588. mnd_sprintf_free(transport.s);
  589. transport.s = NULL;
  590. }
  591. if (!conn->scheme.s) {
  592. goto err; /* OOM */
  593. }
  594. conn->username.l = username.l;
  595. conn->username.s = mnd_pestrndup(username.s, conn->username.l, conn->persistent);
  596. conn->password.l = password.l;
  597. conn->password.s = mnd_pestrndup(password.s, conn->password.l, conn->persistent);
  598. conn->port = port;
  599. conn->connect_or_select_db.l = database.l;
  600. conn->connect_or_select_db.s = mnd_pestrndup(database.s, conn->connect_or_select_db.l, conn->persistent);
  601. if (!conn->username.s || !conn->password.s|| !conn->connect_or_select_db.s) {
  602. SET_OOM_ERROR(conn->error_info);
  603. goto err; /* OOM */
  604. }
  605. if (!unix_socket && !named_pipe) {
  606. conn->hostname.s = mnd_pestrndup(hostname.s, hostname.l, conn->persistent);
  607. if (!conn->hostname.s) {
  608. SET_OOM_ERROR(conn->error_info);
  609. goto err; /* OOM */
  610. }
  611. conn->hostname.l = hostname.l;
  612. {
  613. char *p;
  614. mnd_sprintf(&p, 0, "%s via TCP/IP", conn->hostname.s);
  615. if (!p) {
  616. SET_OOM_ERROR(conn->error_info);
  617. goto err; /* OOM */
  618. }
  619. conn->host_info = mnd_pestrdup(p, conn->persistent);
  620. mnd_sprintf_free(p);
  621. if (!conn->host_info) {
  622. SET_OOM_ERROR(conn->error_info);
  623. goto err; /* OOM */
  624. }
  625. }
  626. } else {
  627. conn->unix_socket.s = mnd_pestrdup(socket_or_pipe.s, conn->persistent);
  628. if (unix_socket) {
  629. conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
  630. } else if (named_pipe) {
  631. char *p;
  632. mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket.s);
  633. if (!p) {
  634. SET_OOM_ERROR(conn->error_info);
  635. goto err; /* OOM */
  636. }
  637. conn->host_info = mnd_pestrdup(p, conn->persistent);
  638. mnd_sprintf_free(p);
  639. if (!conn->host_info) {
  640. SET_OOM_ERROR(conn->error_info);
  641. goto err; /* OOM */
  642. }
  643. } else {
  644. php_error_docref(NULL, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
  645. }
  646. if (!conn->unix_socket.s || !conn->host_info) {
  647. SET_OOM_ERROR(conn->error_info);
  648. goto err; /* OOM */
  649. }
  650. conn->unix_socket.l = strlen(conn->unix_socket.s);
  651. }
  652. SET_EMPTY_ERROR(conn->error_info);
  653. mysqlnd_local_infile_default(conn);
  654. if (FAIL == conn->m->execute_init_commands(conn)) {
  655. goto err;
  656. }
  657. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
  658. if (reconnect) {
  659. MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
  660. }
  661. if (conn->persistent) {
  662. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
  663. }
  664. DBG_INF_FMT("connection_id=%llu", conn->thread_id);
  665. conn->m->local_tx_end(conn, this_func, PASS);
  666. DBG_RETURN(PASS);
  667. }
  668. err:
  669. if (transport.s) {
  670. mnd_sprintf_free(transport.s);
  671. transport.s = NULL;
  672. }
  673. DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
  674. if (!conn->error_info->error_no) {
  675. SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
  676. php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
  677. }
  678. conn->m->free_contents(conn);
  679. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
  680. if (TRUE == local_tx_started) {
  681. conn->m->local_tx_end(conn, this_func, FAIL);
  682. }
  683. DBG_RETURN(FAIL);
  684. }
  685. /* }}} */
  686. /* {{{ mysqlnd_conn::connect */
  687. static enum_func_status
  688. MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
  689. const MYSQLND_CSTRING hostname,
  690. const MYSQLND_CSTRING username,
  691. const MYSQLND_CSTRING password,
  692. const MYSQLND_CSTRING database,
  693. unsigned int port,
  694. const MYSQLND_CSTRING socket_or_pipe,
  695. unsigned int mysql_flags)
  696. {
  697. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
  698. enum_func_status ret = FAIL;
  699. MYSQLND_CONN_DATA * conn = conn_handle->data;
  700. DBG_ENTER("mysqlnd_conn::connect");
  701. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  702. mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
  703. if (hostname.l > 0) {
  704. mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", hostname.s);
  705. }
  706. ret = conn->m->connect(conn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
  707. conn->m->local_tx_end(conn, this_func, FAIL);
  708. }
  709. DBG_RETURN(ret);
  710. }
  711. /* }}} */
  712. /* {{{ mysqlnd_conn_data::query */
  713. /*
  714. If conn->error_info->error_no is not zero, then we had an error.
  715. Still the result from the query is PASS
  716. */
  717. static enum_func_status
  718. MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len)
  719. {
  720. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), query);
  721. enum_func_status ret = FAIL;
  722. DBG_ENTER("mysqlnd_conn_data::query");
  723. DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
  724. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  725. if (PASS == conn->m->send_query(conn, query, query_len, MYSQLND_SEND_QUERY_IMPLICIT, NULL, NULL) &&
  726. PASS == conn->m->reap_query(conn, MYSQLND_REAP_RESULT_IMPLICIT))
  727. {
  728. ret = PASS;
  729. if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
  730. MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
  731. }
  732. }
  733. conn->m->local_tx_end(conn, this_func, ret);
  734. }
  735. DBG_RETURN(ret);
  736. }
  737. /* }}} */
  738. /* {{{ mysqlnd_conn_data::send_query */
  739. static enum_func_status
  740. MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len,
  741. enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb)
  742. {
  743. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), send_query);
  744. enum_func_status ret = FAIL;
  745. DBG_ENTER("mysqlnd_conn_data::send_query");
  746. DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
  747. DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
  748. if (type == MYSQLND_SEND_QUERY_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
  749. {
  750. const MYSQLND_CSTRING query_string = {query, query_len};
  751. ret = conn->run_command(COM_QUERY, conn, query_string);
  752. if (type == MYSQLND_SEND_QUERY_EXPLICIT) {
  753. conn->m->local_tx_end(conn, this_func, ret);
  754. }
  755. }
  756. DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
  757. DBG_RETURN(ret);
  758. }
  759. /* }}} */
  760. /* {{{ mysqlnd_conn_data::reap_query */
  761. static enum_func_status
  762. MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type)
  763. {
  764. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), reap_query);
  765. enum_func_status ret = FAIL;
  766. DBG_ENTER("mysqlnd_conn_data::reap_query");
  767. DBG_INF_FMT("conn=%llu", conn->thread_id);
  768. DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
  769. if (type == MYSQLND_REAP_RESULT_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
  770. {
  771. ret = conn->run_command(COM_REAP_RESULT, conn);
  772. if (type == MYSQLND_REAP_RESULT_EXPLICIT) {
  773. conn->m->local_tx_end(conn, this_func, ret);
  774. }
  775. }
  776. DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
  777. DBG_RETURN(ret);
  778. }
  779. /* }}} */
  780. /* {{{ mysqlnd_conn_data::list_method */
  781. MYSQLND_RES *
  782. MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1)
  783. {
  784. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), list_method);
  785. char * show_query = NULL;
  786. size_t show_query_len;
  787. MYSQLND_RES * result = NULL;
  788. DBG_ENTER("mysqlnd_conn_data::list_method");
  789. DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
  790. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  791. if (par1) {
  792. if (achtung_wild) {
  793. show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
  794. } else {
  795. show_query_len = mnd_sprintf(&show_query, 0, query, par1);
  796. }
  797. } else {
  798. if (achtung_wild) {
  799. show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
  800. } else {
  801. show_query_len = strlen(show_query = (char *)query);
  802. }
  803. }
  804. if (PASS == conn->m->query(conn, show_query, show_query_len)) {
  805. result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY);
  806. }
  807. if (show_query != query) {
  808. mnd_sprintf_free(show_query);
  809. }
  810. conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
  811. }
  812. DBG_RETURN(result);
  813. }
  814. /* }}} */
  815. /* {{{ mysqlnd_conn_data::err_no */
  816. static unsigned int
  817. MYSQLND_METHOD(mysqlnd_conn_data, err_no)(const MYSQLND_CONN_DATA * const conn)
  818. {
  819. return conn->error_info->error_no;
  820. }
  821. /* }}} */
  822. /* {{{ mysqlnd_conn_data::error */
  823. static const char *
  824. MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn)
  825. {
  826. return conn->error_info->error;
  827. }
  828. /* }}} */
  829. /* {{{ mysqlnd_conn_data::sqlstate */
  830. static const char *
  831. MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn)
  832. {
  833. return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
  834. }
  835. /* }}} */
  836. /* {{{ mysqlnd_old_escape_string */
  837. PHPAPI zend_ulong
  838. mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len)
  839. {
  840. DBG_ENTER("mysqlnd_old_escape_string");
  841. DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len));
  842. }
  843. /* }}} */
  844. /* {{{ mysqlnd_conn_data::ssl_set */
  845. static enum_func_status
  846. MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
  847. const char * const ca, const char * const capath, const char * const cipher)
  848. {
  849. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ssl_set);
  850. enum_func_status ret = FAIL;
  851. MYSQLND_VIO * vio = conn->vio;
  852. DBG_ENTER("mysqlnd_conn_data::ssl_set");
  853. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  854. ret = (PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_KEY, key) &&
  855. PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CERT, cert) &&
  856. PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CA, ca) &&
  857. PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CAPATH, capath) &&
  858. PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
  859. conn->m->local_tx_end(conn, this_func, ret);
  860. }
  861. DBG_RETURN(ret);
  862. }
  863. /* }}} */
  864. /* {{{ mysqlnd_conn_data::escape_string */
  865. static zend_ulong
  866. MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len)
  867. {
  868. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), escape_string);
  869. zend_ulong ret = FAIL;
  870. DBG_ENTER("mysqlnd_conn_data::escape_string");
  871. DBG_INF_FMT("conn=%llu", conn->thread_id);
  872. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  873. DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
  874. if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
  875. ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
  876. } else {
  877. ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
  878. }
  879. conn->m->local_tx_end(conn, this_func, PASS);
  880. }
  881. DBG_RETURN(ret);
  882. }
  883. /* }}} */
  884. /* {{{ mysqlnd_conn_data::dump_debug_info */
  885. static enum_func_status
  886. MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn)
  887. {
  888. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), server_dump_debug_information);
  889. enum_func_status ret = FAIL;
  890. DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
  891. DBG_INF_FMT("conn=%llu", conn->thread_id);
  892. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  893. ret = conn->run_command(COM_DEBUG, conn);
  894. conn->m->local_tx_end(conn, this_func, ret);
  895. }
  896. DBG_RETURN(ret);
  897. }
  898. /* }}} */
  899. /* {{{ mysqlnd_conn_data::select_db */
  900. static enum_func_status
  901. MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len)
  902. {
  903. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), select_db);
  904. enum_func_status ret = FAIL;
  905. DBG_ENTER("mysqlnd_conn_data::select_db");
  906. DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
  907. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  908. const MYSQLND_CSTRING database = {db, db_len};
  909. ret = conn->run_command(COM_INIT_DB, conn, database);
  910. conn->m->local_tx_end(conn, this_func, ret);
  911. }
  912. DBG_RETURN(ret);
  913. }
  914. /* }}} */
  915. /* {{{ mysqlnd_conn_data::ping */
  916. static enum_func_status
  917. MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
  918. {
  919. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ping);
  920. enum_func_status ret = FAIL;
  921. DBG_ENTER("mysqlnd_conn_data::ping");
  922. DBG_INF_FMT("conn=%llu", conn->thread_id);
  923. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  924. ret = conn->run_command(COM_PING, conn);
  925. conn->m->local_tx_end(conn, this_func, ret);
  926. }
  927. DBG_INF_FMT("ret=%u", ret);
  928. DBG_RETURN(ret);
  929. }
  930. /* }}} */
  931. /* {{{ mysqlnd_conn_data::statistic */
  932. static enum_func_status
  933. MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_string **message)
  934. {
  935. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), get_server_statistics);
  936. enum_func_status ret = FAIL;
  937. DBG_ENTER("mysqlnd_conn_data::statistic");
  938. DBG_INF_FMT("conn=%llu", conn->thread_id);
  939. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  940. ret = conn->run_command(COM_STATISTICS, conn, message);
  941. conn->m->local_tx_end(conn, this_func, ret);
  942. }
  943. DBG_RETURN(ret);
  944. }
  945. /* }}} */
  946. /* {{{ mysqlnd_conn_data::kill */
  947. static enum_func_status
  948. MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid)
  949. {
  950. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), kill_connection);
  951. enum_func_status ret = FAIL;
  952. DBG_ENTER("mysqlnd_conn_data::kill");
  953. DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
  954. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  955. unsigned int process_id = pid;
  956. /* 'unsigned char' is promoted to 'int' when passed through '...' */
  957. unsigned int read_response = (pid != conn->thread_id);
  958. ret = conn->run_command(COM_PROCESS_KILL, conn, process_id, read_response);
  959. conn->m->local_tx_end(conn, this_func, ret);
  960. }
  961. DBG_RETURN(ret);
  962. }
  963. /* }}} */
  964. /* {{{ mysqlnd_conn_data::set_charset */
  965. static enum_func_status
  966. MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname)
  967. {
  968. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_charset);
  969. enum_func_status ret = FAIL;
  970. const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
  971. DBG_ENTER("mysqlnd_conn_data::set_charset");
  972. DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
  973. if (!charset) {
  974. SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
  975. "Invalid characterset or character set not supported");
  976. DBG_RETURN(ret);
  977. }
  978. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  979. char * query;
  980. size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
  981. if (FAIL == (ret = conn->m->query(conn, query, query_len))) {
  982. php_error_docref(NULL, E_WARNING, "Error executing query");
  983. } else if (conn->error_info->error_no) {
  984. ret = FAIL;
  985. } else {
  986. conn->charset = charset;
  987. }
  988. mnd_sprintf_free(query);
  989. conn->m->local_tx_end(conn, this_func, ret);
  990. }
  991. DBG_INF(ret == PASS? "PASS":"FAIL");
  992. DBG_RETURN(ret);
  993. }
  994. /* }}} */
  995. /* {{{ mysqlnd_conn_data::refresh */
  996. static enum_func_status
  997. MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options)
  998. {
  999. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), refresh_server);
  1000. enum_func_status ret = FAIL;
  1001. DBG_ENTER("mysqlnd_conn_data::refresh");
  1002. DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
  1003. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1004. unsigned int options_param = (unsigned int) options;
  1005. ret = conn->run_command(COM_REFRESH, conn, options_param);
  1006. conn->m->local_tx_end(conn, this_func, ret);
  1007. }
  1008. DBG_RETURN(ret);
  1009. }
  1010. /* }}} */
  1011. /* {{{ mysqlnd_conn_data::shutdown */
  1012. static enum_func_status
  1013. MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level)
  1014. {
  1015. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), shutdown_server);
  1016. enum_func_status ret = FAIL;
  1017. DBG_ENTER("mysqlnd_conn_data::shutdown");
  1018. DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
  1019. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1020. unsigned int level_param = (unsigned int) level;
  1021. ret = conn->run_command(COM_SHUTDOWN, conn, level_param);
  1022. conn->m->local_tx_end(conn, this_func, ret);
  1023. }
  1024. DBG_RETURN(ret);
  1025. }
  1026. /* }}} */
  1027. /* {{{ mysqlnd_send_close */
  1028. static enum_func_status
  1029. MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
  1030. {
  1031. enum_func_status ret = PASS;
  1032. MYSQLND_VIO * vio = conn->vio;
  1033. php_stream * net_stream = vio->data->m.get_stream(vio);
  1034. enum mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
  1035. DBG_ENTER("mysqlnd_send_close");
  1036. DBG_INF_FMT("conn=%llu vio->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
  1037. DBG_INF_FMT("state=%u", state);
  1038. if (state >= CONN_READY) {
  1039. MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_CONNECTIONS);
  1040. if (conn->persistent) {
  1041. MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_PERSISTENT_CONNECTIONS);
  1042. }
  1043. }
  1044. switch (state) {
  1045. case CONN_READY:
  1046. DBG_INF("Connection clean, sending COM_QUIT");
  1047. if (net_stream) {
  1048. ret = conn->run_command(COM_QUIT, conn);
  1049. vio->data->m.close_stream(vio, conn->stats, conn->error_info);
  1050. }
  1051. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  1052. break;
  1053. case CONN_SENDING_LOAD_DATA:
  1054. /*
  1055. Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
  1056. will crash (assert) a debug server.
  1057. */
  1058. case CONN_NEXT_RESULT_PENDING:
  1059. case CONN_QUERY_SENT:
  1060. case CONN_FETCHING_DATA:
  1061. MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
  1062. DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme.s);
  1063. /*
  1064. Do nothing, the connection will be brutally closed
  1065. and the server will catch it and free close from its side.
  1066. */
  1067. /* Fall-through */
  1068. case CONN_ALLOCED:
  1069. /*
  1070. Allocated but not connected or there was failure when trying
  1071. to connect with pre-allocated connect.
  1072. Fall-through
  1073. */
  1074. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  1075. /* Fall-through */
  1076. case CONN_QUIT_SENT:
  1077. /* The user has killed its own connection */
  1078. vio->data->m.close_stream(vio, conn->stats, conn->error_info);
  1079. break;
  1080. }
  1081. DBG_RETURN(ret);
  1082. }
  1083. /* }}} */
  1084. /* {{{ mysqlnd_conn_data::get_reference */
  1085. static MYSQLND_CONN_DATA *
  1086. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn)
  1087. {
  1088. DBG_ENTER("mysqlnd_conn_data::get_reference");
  1089. ++conn->refcount;
  1090. DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
  1091. DBG_RETURN(conn);
  1092. }
  1093. /* }}} */
  1094. /* {{{ mysqlnd_conn_data::free_reference */
  1095. static enum_func_status
  1096. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn)
  1097. {
  1098. enum_func_status ret = PASS;
  1099. DBG_ENTER("mysqlnd_conn_data::free_reference");
  1100. DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
  1101. if (!(--conn->refcount)) {
  1102. /*
  1103. No multithreading issues as we don't share the connection :)
  1104. This will free the object too, of course because references has
  1105. reached zero.
  1106. */
  1107. ret = conn->m->send_close(conn);
  1108. conn->m->dtor(conn);
  1109. }
  1110. DBG_RETURN(ret);
  1111. }
  1112. /* }}} */
  1113. /* {{{ mysqlnd_conn_data::field_count */
  1114. static unsigned int
  1115. MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn)
  1116. {
  1117. return conn->field_count;
  1118. }
  1119. /* }}} */
  1120. /* {{{ mysqlnd_conn_data::server_status */
  1121. static unsigned int
  1122. MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn)
  1123. {
  1124. return UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status);
  1125. }
  1126. /* }}} */
  1127. /* {{{ mysqlnd_conn_data::insert_id */
  1128. static uint64_t
  1129. MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn)
  1130. {
  1131. return UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status);
  1132. }
  1133. /* }}} */
  1134. /* {{{ mysqlnd_conn_data::affected_rows */
  1135. static uint64_t
  1136. MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn)
  1137. {
  1138. return UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status);
  1139. }
  1140. /* }}} */
  1141. /* {{{ mysqlnd_conn_data::warning_count */
  1142. static unsigned int
  1143. MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn)
  1144. {
  1145. return UPSERT_STATUS_GET_WARNINGS(conn->upsert_status);
  1146. }
  1147. /* }}} */
  1148. /* {{{ mysqlnd_conn_data::info */
  1149. static const char *
  1150. MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn)
  1151. {
  1152. return conn->last_message.s;
  1153. }
  1154. /* }}} */
  1155. /* {{{ mysqlnd_get_client_info */
  1156. PHPAPI const char * mysqlnd_get_client_info()
  1157. {
  1158. return PHP_MYSQLND_VERSION;
  1159. }
  1160. /* }}} */
  1161. /* {{{ mysqlnd_get_client_version */
  1162. PHPAPI unsigned long mysqlnd_get_client_version()
  1163. {
  1164. return MYSQLND_VERSION_ID;
  1165. }
  1166. /* }}} */
  1167. /* {{{ mysqlnd_conn_data::get_server_info */
  1168. static const char *
  1169. MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn)
  1170. {
  1171. return conn->server_version;
  1172. }
  1173. /* }}} */
  1174. /* {{{ mysqlnd_conn_data::get_host_info */
  1175. static const char *
  1176. MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn)
  1177. {
  1178. return conn->host_info;
  1179. }
  1180. /* }}} */
  1181. /* {{{ mysqlnd_conn_data::get_proto_info */
  1182. static unsigned int
  1183. MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn)
  1184. {
  1185. return conn->protocol_version;
  1186. }
  1187. /* }}} */
  1188. /* {{{ mysqlnd_conn_data::charset_name */
  1189. static const char *
  1190. MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn)
  1191. {
  1192. return conn->charset->name;
  1193. }
  1194. /* }}} */
  1195. /* {{{ mysqlnd_conn_data::thread_id */
  1196. static uint64_t
  1197. MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn)
  1198. {
  1199. return conn->thread_id;
  1200. }
  1201. /* }}} */
  1202. /* {{{ mysqlnd_conn_data::get_server_version */
  1203. static zend_ulong
  1204. MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn)
  1205. {
  1206. zend_long major, minor, patch;
  1207. char *p;
  1208. if (!(p = conn->server_version)) {
  1209. return 0;
  1210. }
  1211. #define MARIA_DB_VERSION_HACK_PREFIX "5.5.5-"
  1212. if (conn->server_capabilities & CLIENT_PLUGIN_AUTH
  1213. && !strncmp(p, MARIA_DB_VERSION_HACK_PREFIX, sizeof(MARIA_DB_VERSION_HACK_PREFIX)-1))
  1214. {
  1215. p += sizeof(MARIA_DB_VERSION_HACK_PREFIX)-1;
  1216. }
  1217. major = ZEND_STRTOL(p, &p, 10);
  1218. p += 1; /* consume the dot */
  1219. minor = ZEND_STRTOL(p, &p, 10);
  1220. p += 1; /* consume the dot */
  1221. patch = ZEND_STRTOL(p, &p, 10);
  1222. return (zend_ulong)(major * Z_L(10000) + (zend_ulong)(minor * Z_L(100) + patch));
  1223. }
  1224. /* }}} */
  1225. /* {{{ mysqlnd_conn_data::more_results */
  1226. static zend_bool
  1227. MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn)
  1228. {
  1229. DBG_ENTER("mysqlnd_conn_data::more_results");
  1230. /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
  1231. DBG_RETURN(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
  1232. }
  1233. /* }}} */
  1234. /* {{{ mysqlnd_conn_data::next_result */
  1235. static enum_func_status
  1236. MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
  1237. {
  1238. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), next_result);
  1239. enum_func_status ret = FAIL;
  1240. DBG_ENTER("mysqlnd_conn_data::next_result");
  1241. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1242. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1243. do {
  1244. if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING) {
  1245. break;
  1246. }
  1247. SET_EMPTY_ERROR(conn->error_info);
  1248. UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
  1249. /*
  1250. We are sure that there is a result set, since conn->state is set accordingly
  1251. in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
  1252. */
  1253. if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL))) {
  1254. /*
  1255. There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
  1256. So there are no more results and we should just return FALSE, error_no has been set
  1257. */
  1258. if (!conn->error_info->error_no) {
  1259. DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
  1260. php_error_docref(NULL, E_WARNING, "Serious error. PID=%d", getpid());
  1261. SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
  1262. conn->m->send_close(conn);
  1263. } else {
  1264. DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
  1265. }
  1266. break;
  1267. }
  1268. if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
  1269. MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
  1270. }
  1271. } while (0);
  1272. conn->m->local_tx_end(conn, this_func, ret);
  1273. }
  1274. DBG_RETURN(ret);
  1275. }
  1276. /* }}} */
  1277. /* {{{ mysqlnd_field_type_name */
  1278. PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type)
  1279. {
  1280. switch(field_type) {
  1281. case FIELD_TYPE_JSON:
  1282. return "json";
  1283. case FIELD_TYPE_STRING:
  1284. case FIELD_TYPE_VAR_STRING:
  1285. return "string";
  1286. case FIELD_TYPE_TINY:
  1287. case FIELD_TYPE_SHORT:
  1288. case FIELD_TYPE_LONG:
  1289. case FIELD_TYPE_LONGLONG:
  1290. case FIELD_TYPE_INT24:
  1291. return "int";
  1292. case FIELD_TYPE_FLOAT:
  1293. case FIELD_TYPE_DOUBLE:
  1294. case FIELD_TYPE_DECIMAL:
  1295. case FIELD_TYPE_NEWDECIMAL:
  1296. return "real";
  1297. case FIELD_TYPE_TIMESTAMP:
  1298. return "timestamp";
  1299. case FIELD_TYPE_YEAR:
  1300. return "year";
  1301. case FIELD_TYPE_DATE:
  1302. case FIELD_TYPE_NEWDATE:
  1303. return "date";
  1304. case FIELD_TYPE_TIME:
  1305. return "time";
  1306. case FIELD_TYPE_SET:
  1307. return "set";
  1308. case FIELD_TYPE_ENUM:
  1309. return "enum";
  1310. case FIELD_TYPE_GEOMETRY:
  1311. return "geometry";
  1312. case FIELD_TYPE_DATETIME:
  1313. return "datetime";
  1314. case FIELD_TYPE_TINY_BLOB:
  1315. case FIELD_TYPE_MEDIUM_BLOB:
  1316. case FIELD_TYPE_LONG_BLOB:
  1317. case FIELD_TYPE_BLOB:
  1318. return "blob";
  1319. case FIELD_TYPE_NULL:
  1320. return "null";
  1321. case FIELD_TYPE_BIT:
  1322. return "bit";
  1323. default:
  1324. return "unknown";
  1325. }
  1326. }
  1327. /* }}} */
  1328. /* {{{ mysqlnd_conn_data::change_user */
  1329. static enum_func_status
  1330. MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
  1331. const char * user,
  1332. const char * passwd,
  1333. const char * db,
  1334. zend_bool silent,
  1335. size_t passwd_len
  1336. )
  1337. {
  1338. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), change_user);
  1339. enum_func_status ret = FAIL;
  1340. DBG_ENTER("mysqlnd_conn_data::change_user");
  1341. DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
  1342. conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
  1343. if (PASS != conn->m->local_tx_start(conn, this_func)) {
  1344. goto end;
  1345. }
  1346. SET_EMPTY_ERROR(conn->error_info);
  1347. UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
  1348. if (!user) {
  1349. user = "";
  1350. }
  1351. if (!passwd) {
  1352. passwd = "";
  1353. passwd_len = 0;
  1354. }
  1355. if (!db) {
  1356. db = "";
  1357. }
  1358. /* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
  1359. ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, strlen(db),
  1360. conn->authentication_plugin_data, conn->options->auth_protocol,
  1361. 0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/);
  1362. /*
  1363. Here we should close all statements. Unbuffered queries should not be a
  1364. problem as we won't allow sending COM_CHANGE_USER.
  1365. */
  1366. conn->m->local_tx_end(conn, this_func, ret);
  1367. end:
  1368. DBG_INF(ret == PASS? "PASS":"FAIL");
  1369. DBG_RETURN(ret);
  1370. }
  1371. /* }}} */
  1372. /* {{{ mysqlnd_conn_data::set_client_option */
  1373. static enum_func_status
  1374. MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
  1375. enum_mysqlnd_client_option option,
  1376. const char * const value
  1377. )
  1378. {
  1379. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option);
  1380. enum_func_status ret = PASS;
  1381. DBG_ENTER("mysqlnd_conn_data::set_client_option");
  1382. DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
  1383. if (PASS != conn->m->local_tx_start(conn, this_func)) {
  1384. goto end;
  1385. }
  1386. switch (option) {
  1387. case MYSQL_OPT_READ_TIMEOUT:
  1388. case MYSQL_OPT_WRITE_TIMEOUT:
  1389. case MYSQLND_OPT_SSL_KEY:
  1390. case MYSQLND_OPT_SSL_CERT:
  1391. case MYSQLND_OPT_SSL_CA:
  1392. case MYSQLND_OPT_SSL_CAPATH:
  1393. case MYSQLND_OPT_SSL_CIPHER:
  1394. case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
  1395. case MYSQL_OPT_CONNECT_TIMEOUT:
  1396. case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
  1397. ret = conn->vio->data->m.set_client_option(conn->vio, option, value);
  1398. break;
  1399. case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
  1400. case MYSQL_OPT_COMPRESS:
  1401. case MYSQL_SERVER_PUBLIC_KEY:
  1402. ret = conn->protocol_frame_codec->data->m.set_client_option(conn->protocol_frame_codec, option, value);
  1403. break;
  1404. #ifdef MYSQLND_STRING_TO_INT_CONVERSION
  1405. case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
  1406. conn->options->int_and_float_native = *(unsigned int*) value;
  1407. break;
  1408. #endif
  1409. case MYSQL_OPT_LOCAL_INFILE:
  1410. if (value && (*(unsigned int*) value) ? 1 : 0) {
  1411. conn->options->flags |= CLIENT_LOCAL_FILES;
  1412. } else {
  1413. conn->options->flags &= ~CLIENT_LOCAL_FILES;
  1414. }
  1415. break;
  1416. case MYSQL_INIT_COMMAND:
  1417. {
  1418. char ** new_init_commands;
  1419. char * new_command;
  1420. /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
  1421. /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
  1422. new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
  1423. if (!new_init_commands) {
  1424. goto oom;
  1425. }
  1426. conn->options->init_commands = new_init_commands;
  1427. new_command = mnd_pestrdup(value, conn->persistent);
  1428. if (!new_command) {
  1429. goto oom;
  1430. }
  1431. conn->options->init_commands[conn->options->num_commands] = new_command;
  1432. ++conn->options->num_commands;
  1433. break;
  1434. }
  1435. case MYSQL_READ_DEFAULT_FILE:
  1436. case MYSQL_READ_DEFAULT_GROUP:
  1437. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1438. case MYSQL_SET_CLIENT_IP:
  1439. case MYSQL_REPORT_DATA_TRUNCATION:
  1440. #endif
  1441. /* currently not supported. Todo!! */
  1442. break;
  1443. case MYSQL_SET_CHARSET_NAME:
  1444. {
  1445. char * new_charset_name;
  1446. if (!mysqlnd_find_charset_name(value)) {
  1447. SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
  1448. ret = FAIL;
  1449. break;
  1450. }
  1451. new_charset_name = mnd_pestrdup(value, conn->persistent);
  1452. if (!new_charset_name) {
  1453. goto oom;
  1454. }
  1455. if (conn->options->charset_name) {
  1456. mnd_pefree(conn->options->charset_name, conn->persistent);
  1457. }
  1458. conn->options->charset_name = new_charset_name;
  1459. DBG_INF_FMT("charset=%s", conn->options->charset_name);
  1460. break;
  1461. }
  1462. case MYSQL_OPT_NAMED_PIPE:
  1463. conn->options->protocol = MYSQL_PROTOCOL_PIPE;
  1464. break;
  1465. case MYSQL_OPT_PROTOCOL:
  1466. if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
  1467. conn->options->protocol = *(unsigned int*) value;
  1468. }
  1469. break;
  1470. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1471. case MYSQL_SET_CHARSET_DIR:
  1472. case MYSQL_OPT_RECONNECT:
  1473. /* we don't need external character sets, all character sets are
  1474. compiled in. For compatibility we just ignore this setting.
  1475. Same for protocol, we don't support old protocol */
  1476. case MYSQL_OPT_USE_REMOTE_CONNECTION:
  1477. case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
  1478. case MYSQL_OPT_GUESS_CONNECTION:
  1479. /* todo: throw an error, we don't support embedded */
  1480. break;
  1481. #endif
  1482. case MYSQLND_OPT_MAX_ALLOWED_PACKET:
  1483. if (*(unsigned int*) value > (1<<16)) {
  1484. conn->options->max_allowed_packet = *(unsigned int*) value;
  1485. }
  1486. break;
  1487. case MYSQLND_OPT_AUTH_PROTOCOL:
  1488. {
  1489. char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
  1490. if (value && !new_auth_protocol) {
  1491. goto oom;
  1492. }
  1493. if (conn->options->auth_protocol) {
  1494. mnd_pefree(conn->options->auth_protocol, conn->persistent);
  1495. }
  1496. conn->options->auth_protocol = new_auth_protocol;
  1497. DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
  1498. break;
  1499. }
  1500. case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
  1501. if (value && (*(unsigned int*) value) ? 1 : 0) {
  1502. conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
  1503. } else {
  1504. conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
  1505. }
  1506. break;
  1507. case MYSQL_OPT_CONNECT_ATTR_RESET:
  1508. if (conn->options->connect_attr) {
  1509. DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
  1510. zend_hash_clean(conn->options->connect_attr);
  1511. }
  1512. break;
  1513. case MYSQL_OPT_CONNECT_ATTR_DELETE:
  1514. if (conn->options->connect_attr && value) {
  1515. DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
  1516. zend_hash_str_del(conn->options->connect_attr, value, strlen(value));
  1517. DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
  1518. }
  1519. break;
  1520. #ifdef WHEN_SUPPORTED_BY_MYSQLI
  1521. case MYSQL_SHARED_MEMORY_BASE_NAME:
  1522. case MYSQL_OPT_USE_RESULT:
  1523. case MYSQL_SECURE_AUTH:
  1524. /* not sure, todo ? */
  1525. #endif
  1526. default:
  1527. ret = FAIL;
  1528. }
  1529. conn->m->local_tx_end(conn, this_func, ret);
  1530. DBG_RETURN(ret);
  1531. oom:
  1532. SET_OOM_ERROR(conn->error_info);
  1533. conn->m->local_tx_end(conn, this_func, FAIL);
  1534. end:
  1535. DBG_RETURN(FAIL);
  1536. }
  1537. /* }}} */
  1538. /* {{{ mysqlnd_conn_data::set_client_option_2d */
  1539. static enum_func_status
  1540. MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
  1541. const enum_mysqlnd_client_option option,
  1542. const char * const key,
  1543. const char * const value
  1544. )
  1545. {
  1546. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option_2d);
  1547. enum_func_status ret = PASS;
  1548. DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
  1549. DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
  1550. if (PASS != conn->m->local_tx_start(conn, this_func)) {
  1551. goto end;
  1552. }
  1553. switch (option) {
  1554. case MYSQL_OPT_CONNECT_ATTR_ADD:
  1555. if (!conn->options->connect_attr) {
  1556. DBG_INF("Initializing connect_attr hash");
  1557. conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
  1558. if (!conn->options->connect_attr) {
  1559. goto oom;
  1560. }
  1561. zend_hash_init(conn->options->connect_attr, 0, NULL, conn->persistent ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR, conn->persistent);
  1562. }
  1563. DBG_INF_FMT("Adding [%s][%s]", key, value);
  1564. {
  1565. zval attrz;
  1566. zend_string *str = zend_string_init(key, strlen(key), 1);
  1567. GC_MAKE_PERSISTENT_LOCAL(str);
  1568. ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), conn->persistent));
  1569. GC_MAKE_PERSISTENT_LOCAL(Z_COUNTED(attrz));
  1570. zend_hash_update(conn->options->connect_attr, str, &attrz);
  1571. zend_string_release_ex(str, 1);
  1572. }
  1573. break;
  1574. default:
  1575. ret = FAIL;
  1576. }
  1577. conn->m->local_tx_end(conn, this_func, ret);
  1578. DBG_RETURN(ret);
  1579. oom:
  1580. SET_OOM_ERROR(conn->error_info);
  1581. conn->m->local_tx_end(conn, this_func, FAIL);
  1582. end:
  1583. DBG_RETURN(FAIL);
  1584. }
  1585. /* }}} */
  1586. /* {{{ mysqlnd_conn_data::use_result */
  1587. static MYSQLND_RES *
  1588. MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
  1589. {
  1590. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), use_result);
  1591. MYSQLND_RES * result = NULL;
  1592. DBG_ENTER("mysqlnd_conn_data::use_result");
  1593. DBG_INF_FMT("conn=%llu", conn->thread_id);
  1594. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1595. do {
  1596. if (!conn->current_result) {
  1597. break;
  1598. }
  1599. /* Nothing to store for UPSERT/LOAD DATA */
  1600. if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
  1601. SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
  1602. DBG_ERR("Command out of sync");
  1603. break;
  1604. }
  1605. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
  1606. conn->current_result->conn = conn->m->get_reference(conn);
  1607. result = conn->current_result->m.use_result(conn->current_result, FALSE);
  1608. if (!result) {
  1609. conn->current_result->m.free_result(conn->current_result, TRUE);
  1610. }
  1611. conn->current_result = NULL;
  1612. } while (0);
  1613. conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
  1614. }
  1615. DBG_RETURN(result);
  1616. }
  1617. /* }}} */
  1618. /* {{{ mysqlnd_conn_data::store_result */
  1619. static MYSQLND_RES *
  1620. MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
  1621. {
  1622. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), store_result);
  1623. MYSQLND_RES * result = NULL;
  1624. DBG_ENTER("mysqlnd_conn_data::store_result");
  1625. DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
  1626. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1627. do {
  1628. unsigned int f = flags;
  1629. if (!conn->current_result) {
  1630. break;
  1631. }
  1632. /* Nothing to store for UPSERT/LOAD DATA*/
  1633. if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
  1634. SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
  1635. DBG_ERR("Command out of sync");
  1636. break;
  1637. }
  1638. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
  1639. /* overwrite */
  1640. if ((conn->m->get_client_api_capabilities(conn) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
  1641. if (MYSQLND_G(fetch_data_copy)) {
  1642. f &= ~MYSQLND_STORE_NO_COPY;
  1643. f |= MYSQLND_STORE_COPY;
  1644. }
  1645. } else {
  1646. /* if for some reason PDO borks something */
  1647. if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
  1648. f |= MYSQLND_STORE_COPY;
  1649. }
  1650. }
  1651. if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
  1652. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
  1653. DBG_ERR("Unknown fetch mode");
  1654. break;
  1655. }
  1656. result = conn->current_result->m.store_result(conn->current_result, conn, f);
  1657. if (!result) {
  1658. conn->current_result->m.free_result(conn->current_result, TRUE);
  1659. }
  1660. conn->current_result = NULL;
  1661. } while (0);
  1662. conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
  1663. }
  1664. DBG_RETURN(result);
  1665. }
  1666. /* }}} */
  1667. /* {{{ mysqlnd_conn_data::get_connection_stats */
  1668. static void
  1669. MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
  1670. zval * return_value ZEND_FILE_LINE_DC)
  1671. {
  1672. DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
  1673. mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
  1674. DBG_VOID_RETURN;
  1675. }
  1676. /* }}} */
  1677. /* {{{ mysqlnd_conn_data::set_autocommit */
  1678. static enum_func_status
  1679. MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode)
  1680. {
  1681. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_autocommit);
  1682. enum_func_status ret = FAIL;
  1683. DBG_ENTER("mysqlnd_conn_data::set_autocommit");
  1684. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1685. ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1);
  1686. conn->m->local_tx_end(conn, this_func, ret);
  1687. }
  1688. DBG_RETURN(ret);
  1689. }
  1690. /* }}} */
  1691. /* {{{ mysqlnd_conn_data::tx_commit */
  1692. static enum_func_status
  1693. MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn)
  1694. {
  1695. return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL);
  1696. }
  1697. /* }}} */
  1698. /* {{{ mysqlnd_conn_data::tx_rollback */
  1699. static enum_func_status
  1700. MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn)
  1701. {
  1702. return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL);
  1703. }
  1704. /* }}} */
  1705. /* {{{ mysqlnd_tx_cor_options_to_string */
  1706. static void
  1707. MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode)
  1708. {
  1709. if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
  1710. if (str->s && ZSTR_LEN(str->s)) {
  1711. smart_str_appendl(str, " ", sizeof(" ") - 1);
  1712. }
  1713. smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
  1714. } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
  1715. if (str->s && ZSTR_LEN(str->s)) {
  1716. smart_str_appendl(str, " ", sizeof(" ") - 1);
  1717. }
  1718. smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
  1719. }
  1720. if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
  1721. if (str->s && ZSTR_LEN(str->s)) {
  1722. smart_str_appendl(str, " ", sizeof(" ") - 1);
  1723. }
  1724. smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
  1725. } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
  1726. if (str->s && ZSTR_LEN(str->s)) {
  1727. smart_str_appendl(str, " ", sizeof(" ") - 1);
  1728. }
  1729. smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
  1730. }
  1731. smart_str_0(str);
  1732. }
  1733. /* }}} */
  1734. /* {{{ mysqlnd_escape_string_for_tx_name_in_comment */
  1735. static char *
  1736. mysqlnd_escape_string_for_tx_name_in_comment(const char * const name)
  1737. {
  1738. char * ret = NULL;
  1739. DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
  1740. if (name) {
  1741. zend_bool warned = FALSE;
  1742. const char * p_orig = name;
  1743. char * p_copy;
  1744. p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */
  1745. *p_copy++ = ' ';
  1746. *p_copy++ = '/';
  1747. *p_copy++ = '*';
  1748. while (1) {
  1749. register char v = *p_orig;
  1750. if (v == 0) {
  1751. break;
  1752. }
  1753. if ((v >= '0' && v <= '9') ||
  1754. (v >= 'a' && v <= 'z') ||
  1755. (v >= 'A' && v <= 'Z') ||
  1756. v == '-' ||
  1757. v == '_' ||
  1758. v == ' ' ||
  1759. v == '=')
  1760. {
  1761. *p_copy++ = v;
  1762. } else if (warned == FALSE) {
  1763. php_error_docref(NULL, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
  1764. warned = TRUE;
  1765. }
  1766. ++p_orig;
  1767. }
  1768. *p_copy++ = '*';
  1769. *p_copy++ = '/';
  1770. *p_copy++ = 0;
  1771. }
  1772. DBG_RETURN(ret);
  1773. }
  1774. /* }}} */
  1775. /* {{{ mysqlnd_conn_data::tx_commit_ex */
  1776. static enum_func_status
  1777. MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name)
  1778. {
  1779. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_commit_or_rollback);
  1780. enum_func_status ret = FAIL;
  1781. DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
  1782. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1783. do {
  1784. smart_str tmp_str = {0, 0};
  1785. conn->m->tx_cor_options_to_string(conn, &tmp_str, flags);
  1786. smart_str_0(&tmp_str);
  1787. {
  1788. char * query;
  1789. size_t query_len;
  1790. char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
  1791. query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
  1792. name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
  1793. smart_str_free(&tmp_str);
  1794. if (name_esc) {
  1795. mnd_efree(name_esc);
  1796. name_esc = NULL;
  1797. }
  1798. if (!query) {
  1799. SET_OOM_ERROR(conn->error_info);
  1800. break;
  1801. }
  1802. ret = conn->m->query(conn, query, query_len);
  1803. mnd_sprintf_free(query);
  1804. }
  1805. } while (0);
  1806. conn->m->local_tx_end(conn, this_func, ret);
  1807. }
  1808. DBG_RETURN(ret);
  1809. }
  1810. /* }}} */
  1811. /* {{{ mysqlnd_conn_data::tx_begin */
  1812. static enum_func_status
  1813. MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name)
  1814. {
  1815. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_begin);
  1816. enum_func_status ret = FAIL;
  1817. DBG_ENTER("mysqlnd_conn_data::tx_begin");
  1818. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1819. do {
  1820. smart_str tmp_str = {0, 0};
  1821. if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
  1822. if (tmp_str.s) {
  1823. smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
  1824. }
  1825. smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
  1826. }
  1827. if (mode & TRANS_START_READ_WRITE) {
  1828. if (tmp_str.s && ZSTR_LEN(tmp_str.s)) {
  1829. smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
  1830. }
  1831. smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
  1832. } else if (mode & TRANS_START_READ_ONLY) {
  1833. if (tmp_str.s && ZSTR_LEN(tmp_str.s)) {
  1834. smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
  1835. }
  1836. smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
  1837. }
  1838. smart_str_0(&tmp_str);
  1839. {
  1840. char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
  1841. char * query;
  1842. unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
  1843. smart_str_free(&tmp_str);
  1844. if (name_esc) {
  1845. mnd_efree(name_esc);
  1846. name_esc = NULL;
  1847. }
  1848. if (!query) {
  1849. SET_OOM_ERROR(conn->error_info);
  1850. break;
  1851. }
  1852. ret = conn->m->query(conn, query, query_len);
  1853. mnd_sprintf_free(query);
  1854. if (ret && mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY) &&
  1855. mysqlnd_stmt_errno(conn) == 1064) {
  1856. php_error_docref(NULL, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
  1857. break;
  1858. }
  1859. }
  1860. } while (0);
  1861. conn->m->local_tx_end(conn, this_func, ret);
  1862. }
  1863. DBG_RETURN(ret);
  1864. }
  1865. /* }}} */
  1866. /* {{{ mysqlnd_conn_data::tx_savepoint */
  1867. static enum_func_status
  1868. MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name)
  1869. {
  1870. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint);
  1871. enum_func_status ret = FAIL;
  1872. DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
  1873. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1874. do {
  1875. char * query;
  1876. unsigned int query_len;
  1877. if (!name) {
  1878. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
  1879. break;
  1880. }
  1881. query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
  1882. if (!query) {
  1883. SET_OOM_ERROR(conn->error_info);
  1884. break;
  1885. }
  1886. ret = conn->m->query(conn, query, query_len);
  1887. mnd_sprintf_free(query);
  1888. } while (0);
  1889. conn->m->local_tx_end(conn, this_func, ret);
  1890. }
  1891. DBG_RETURN(ret);
  1892. }
  1893. /* }}} */
  1894. /* {{{ mysqlnd_conn_data::tx_savepoint_release */
  1895. static enum_func_status
  1896. MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name)
  1897. {
  1898. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint_release);
  1899. enum_func_status ret = FAIL;
  1900. DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
  1901. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  1902. do {
  1903. char * query;
  1904. unsigned int query_len;
  1905. if (!name) {
  1906. SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
  1907. break;
  1908. }
  1909. query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
  1910. if (!query) {
  1911. SET_OOM_ERROR(conn->error_info);
  1912. break;
  1913. }
  1914. ret = conn->m->query(conn, query, query_len);
  1915. mnd_sprintf_free(query);
  1916. } while (0);
  1917. conn->m->local_tx_end(conn, this_func, ret);
  1918. }
  1919. DBG_RETURN(ret);
  1920. }
  1921. /* }}} */
  1922. /* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
  1923. static size_t
  1924. MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags)
  1925. {
  1926. unsigned int ret = 0;
  1927. DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
  1928. if (conn) {
  1929. ret = conn->client_api_capabilities;
  1930. conn->client_api_capabilities = flags;
  1931. }
  1932. DBG_RETURN(ret);
  1933. }
  1934. /* }}} */
  1935. /* {{{ mysqlnd_conn_data::get_client_api_capabilities */
  1936. static size_t
  1937. MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn)
  1938. {
  1939. DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
  1940. DBG_RETURN(conn? conn->client_api_capabilities : 0);
  1941. }
  1942. /* }}} */
  1943. /* {{{ mysqlnd_conn_data::local_tx_start */
  1944. static enum_func_status
  1945. MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func)
  1946. {
  1947. DBG_ENTER("mysqlnd_conn_data::local_tx_start");
  1948. DBG_RETURN(PASS);
  1949. }
  1950. /* }}} */
  1951. /* {{{ mysqlnd_conn_data::local_tx_end */
  1952. static enum_func_status
  1953. MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status)
  1954. {
  1955. DBG_ENTER("mysqlnd_conn_data::local_tx_end");
  1956. DBG_RETURN(status);
  1957. }
  1958. /* }}} */
  1959. /* {{{ _mysqlnd_stmt_init */
  1960. MYSQLND_STMT *
  1961. MYSQLND_METHOD(mysqlnd_conn_data, stmt_init)(MYSQLND_CONN_DATA * const conn)
  1962. {
  1963. MYSQLND_STMT * ret;
  1964. DBG_ENTER("mysqlnd_conn_data::stmt_init");
  1965. ret = conn->object_factory.get_prepared_statement(conn);
  1966. DBG_RETURN(ret);
  1967. }
  1968. /* }}} */
  1969. MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
  1970. MYSQLND_METHOD(mysqlnd_conn_data, connect),
  1971. MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
  1972. MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
  1973. MYSQLND_METHOD(mysqlnd_conn_data, query),
  1974. MYSQLND_METHOD(mysqlnd_conn_data, send_query),
  1975. MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
  1976. MYSQLND_METHOD(mysqlnd_conn_data, use_result),
  1977. MYSQLND_METHOD(mysqlnd_conn_data, store_result),
  1978. MYSQLND_METHOD(mysqlnd_conn_data, next_result),
  1979. MYSQLND_METHOD(mysqlnd_conn_data, more_results),
  1980. MYSQLND_METHOD(mysqlnd_conn_data, stmt_init),
  1981. MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
  1982. MYSQLND_METHOD(mysqlnd_conn_data, refresh),
  1983. MYSQLND_METHOD(mysqlnd_conn_data, ping),
  1984. MYSQLND_METHOD(mysqlnd_conn_data, kill),
  1985. MYSQLND_METHOD(mysqlnd_conn_data, select_db),
  1986. MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
  1987. MYSQLND_METHOD(mysqlnd_conn_data, change_user),
  1988. MYSQLND_METHOD(mysqlnd_conn_data, err_no),
  1989. MYSQLND_METHOD(mysqlnd_conn_data, error),
  1990. MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
  1991. MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
  1992. MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
  1993. MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
  1994. MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
  1995. MYSQLND_METHOD(mysqlnd_conn_data, statistic),
  1996. MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
  1997. MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
  1998. MYSQLND_METHOD(mysqlnd_conn_data, info),
  1999. MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
  2000. MYSQLND_METHOD(mysqlnd_conn_data, list_method),
  2001. MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
  2002. MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
  2003. MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
  2004. MYSQLND_METHOD(mysqlnd_conn_data, field_count),
  2005. MYSQLND_METHOD(mysqlnd_conn_data, server_status),
  2006. MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
  2007. MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
  2008. MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
  2009. MYSQLND_METHOD(mysqlnd_conn_data, free_options),
  2010. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
  2011. mysqlnd_query_read_result_set_header,
  2012. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
  2013. MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
  2014. MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
  2015. MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
  2016. MYSQLND_METHOD(mysqlnd_conn_data, send_close),
  2017. MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
  2018. mysqlnd_result_init,
  2019. MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
  2020. MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
  2021. MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
  2022. MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
  2023. MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
  2024. MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
  2025. MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
  2026. MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
  2027. MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
  2028. MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
  2029. MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
  2030. MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
  2031. MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
  2032. MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
  2033. MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
  2034. MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
  2035. MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities),
  2036. MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)
  2037. MYSQLND_CLASS_METHODS_END;
  2038. /* {{{ mysqlnd_conn::get_reference */
  2039. static MYSQLND *
  2040. MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn)
  2041. {
  2042. MYSQLND * ret;
  2043. DBG_ENTER("mysqlnd_conn::get_reference");
  2044. ret = conn->data->object_factory.clone_connection_object(conn);
  2045. DBG_RETURN(ret);
  2046. }
  2047. /* }}} */
  2048. /* {{{ mysqlnd_conn_data::dtor */
  2049. static void
  2050. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn)
  2051. {
  2052. DBG_ENTER("mysqlnd_conn::dtor");
  2053. DBG_INF_FMT("conn=%llu", conn->data->thread_id);
  2054. conn->data->m->free_reference(conn->data);
  2055. mnd_pefree(conn, conn->persistent);
  2056. DBG_VOID_RETURN;
  2057. }
  2058. /* }}} */
  2059. /* {{{ mysqlnd_conn_data::close */
  2060. static enum_func_status
  2061. MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, const enum_connection_close_type close_type)
  2062. {
  2063. const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn), close);
  2064. MYSQLND_CONN_DATA * conn = conn_handle->data;
  2065. enum_func_status ret = FAIL;
  2066. DBG_ENTER("mysqlnd_conn::close");
  2067. DBG_INF_FMT("conn=%llu", conn->thread_id);
  2068. if (PASS == conn->m->local_tx_start(conn, this_func)) {
  2069. if (GET_CONNECTION_STATE(&conn->state) >= CONN_READY) {
  2070. static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
  2071. STAT_CLOSE_EXPLICIT,
  2072. STAT_CLOSE_IMPLICIT,
  2073. STAT_CLOSE_DISCONNECT
  2074. };
  2075. MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
  2076. }
  2077. /*
  2078. Close now, free_reference will try,
  2079. if we are last, but that's not a problem.
  2080. */
  2081. ret = conn->m->send_close(conn);
  2082. /* If we do it after free_reference/dtor then we might crash */
  2083. conn->m->local_tx_end(conn, this_func, ret);
  2084. conn_handle->m->dtor(conn_handle);
  2085. }
  2086. DBG_RETURN(ret);
  2087. }
  2088. /* }}} */
  2089. MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
  2090. MYSQLND_METHOD(mysqlnd_conn, connect),
  2091. MYSQLND_METHOD(mysqlnd_conn, clone_object),
  2092. MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
  2093. MYSQLND_METHOD(mysqlnd_conn, close)
  2094. MYSQLND_CLASS_METHODS_END;
  2095. #include "php_network.h"
  2096. /* {{{ mysqlnd_stream_array_to_fd_set */
  2097. MYSQLND **
  2098. mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
  2099. {
  2100. unsigned int cnt = 0;
  2101. MYSQLND **p = conn_array, **p_p;
  2102. MYSQLND **ret = NULL;
  2103. while (*p) {
  2104. const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
  2105. if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
  2106. cnt++;
  2107. }
  2108. p++;
  2109. }
  2110. if (cnt) {
  2111. MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
  2112. p_p = p = conn_array;
  2113. while (*p) {
  2114. const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
  2115. if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
  2116. *ret_p = *p;
  2117. *p = NULL;
  2118. ret_p++;
  2119. } else {
  2120. *p_p = *p;
  2121. p_p++;
  2122. }
  2123. p++;
  2124. }
  2125. *ret_p = NULL;
  2126. }
  2127. return ret;
  2128. }
  2129. /* }}} */
  2130. /* {{{ mysqlnd_stream_array_to_fd_set */
  2131. static unsigned int
  2132. mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
  2133. {
  2134. php_socket_t this_fd;
  2135. php_stream *stream = NULL;
  2136. unsigned int cnt = 0;
  2137. MYSQLND **p = conn_array;
  2138. DBG_ENTER("mysqlnd_stream_array_to_fd_set");
  2139. while (*p) {
  2140. /* get the fd.
  2141. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
  2142. * when casting. It is only used here so that the buffered data warning
  2143. * is not displayed.
  2144. * */
  2145. stream = (*p)->data->vio->data->m.get_stream((*p)->data->vio);
  2146. DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
  2147. if (stream != NULL &&
  2148. SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) &&
  2149. ZEND_VALID_SOCKET(this_fd))
  2150. {
  2151. PHP_SAFE_FD_SET(this_fd, fds);
  2152. if (this_fd > *max_fd) {
  2153. *max_fd = this_fd;
  2154. }
  2155. ++cnt;
  2156. }
  2157. ++p;
  2158. }
  2159. DBG_RETURN(cnt ? 1 : 0);
  2160. }
  2161. /* }}} */
  2162. /* {{{ mysqlnd_stream_array_from_fd_set */
  2163. static unsigned int
  2164. mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
  2165. {
  2166. php_socket_t this_fd;
  2167. php_stream *stream = NULL;
  2168. unsigned int ret = 0;
  2169. zend_bool disproportion = FALSE;
  2170. MYSQLND **fwd = conn_array, **bckwd = conn_array;
  2171. DBG_ENTER("mysqlnd_stream_array_from_fd_set");
  2172. while (*fwd) {
  2173. stream = (*fwd)->data->vio->data->m.get_stream((*fwd)->data->vio);
  2174. DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
  2175. if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
  2176. (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
  2177. if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
  2178. if (disproportion) {
  2179. *bckwd = *fwd;
  2180. }
  2181. ++bckwd;
  2182. ++fwd;
  2183. ++ret;
  2184. continue;
  2185. }
  2186. }
  2187. disproportion = TRUE;
  2188. ++fwd;
  2189. }
  2190. *bckwd = NULL;/* NULL-terminate the list */
  2191. DBG_RETURN(ret);
  2192. }
  2193. /* }}} */
  2194. #ifndef PHP_WIN32
  2195. #define php_select(m, r, w, e, t) select(m, r, w, e, t)
  2196. #else
  2197. #include "win32/select.h"
  2198. #endif
  2199. /* {{{ mysqlnd_poll */
  2200. PHPAPI enum_func_status
  2201. mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
  2202. {
  2203. struct timeval tv;
  2204. struct timeval *tv_p = NULL;
  2205. fd_set rfds, wfds, efds;
  2206. php_socket_t max_fd = 0;
  2207. int retval, sets = 0;
  2208. int set_count, max_set_count = 0;
  2209. DBG_ENTER("_mysqlnd_poll");
  2210. if (sec < 0 || usec < 0) {
  2211. php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
  2212. DBG_RETURN(FAIL);
  2213. }
  2214. FD_ZERO(&rfds);
  2215. FD_ZERO(&wfds);
  2216. FD_ZERO(&efds);
  2217. if (r_array != NULL) {
  2218. *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
  2219. set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
  2220. if (set_count > max_set_count) {
  2221. max_set_count = set_count;
  2222. }
  2223. sets += set_count;
  2224. }
  2225. if (e_array != NULL) {
  2226. set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
  2227. if (set_count > max_set_count) {
  2228. max_set_count = set_count;
  2229. }
  2230. sets += set_count;
  2231. }
  2232. if (!sets) {
  2233. php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
  2234. DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
  2235. DBG_RETURN(FAIL);
  2236. }
  2237. PHP_SAFE_MAX_FD(max_fd, max_set_count);
  2238. /* Solaris + BSD do not like microsecond values which are >= 1 sec */
  2239. if (usec > 999999) {
  2240. tv.tv_sec = sec + (usec / 1000000);
  2241. tv.tv_usec = usec % 1000000;
  2242. } else {
  2243. tv.tv_sec = sec;
  2244. tv.tv_usec = usec;
  2245. }
  2246. tv_p = &tv;
  2247. retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
  2248. if (retval == -1) {
  2249. php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
  2250. errno, strerror(errno), max_fd);
  2251. DBG_RETURN(FAIL);
  2252. }
  2253. if (r_array != NULL) {
  2254. mysqlnd_stream_array_from_fd_set(r_array, &rfds);
  2255. }
  2256. if (e_array != NULL) {
  2257. mysqlnd_stream_array_from_fd_set(e_array, &efds);
  2258. }
  2259. *desc_num = retval;
  2260. DBG_RETURN(PASS);
  2261. }
  2262. /* }}} */
  2263. /* {{{ mysqlnd_connect */
  2264. PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn_handle,
  2265. const char * const host,
  2266. const char * const user,
  2267. const char * const passwd, unsigned int passwd_len,
  2268. const char * const db, unsigned int db_len,
  2269. unsigned int port,
  2270. const char * const sock_or_pipe,
  2271. unsigned int mysql_flags,
  2272. unsigned int client_api_flags
  2273. )
  2274. {
  2275. enum_func_status ret = FAIL;
  2276. zend_bool self_alloced = FALSE;
  2277. MYSQLND_CSTRING hostname = { host, host? strlen(host) : 0 };
  2278. MYSQLND_CSTRING username = { user, user? strlen(user) : 0 };
  2279. MYSQLND_CSTRING password = { passwd, passwd_len };
  2280. MYSQLND_CSTRING database = { db, db_len };
  2281. MYSQLND_CSTRING socket_or_pipe = { sock_or_pipe, sock_or_pipe? strlen(sock_or_pipe) : 0 };
  2282. DBG_ENTER("mysqlnd_connect");
  2283. DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host? host:"", user? user:"", db? db:"", port, mysql_flags);
  2284. if (!conn_handle) {
  2285. self_alloced = TRUE;
  2286. if (!(conn_handle = mysqlnd_connection_init(client_api_flags, FALSE, NULL))) {
  2287. /* OOM */
  2288. DBG_RETURN(NULL);
  2289. }
  2290. }
  2291. ret = conn_handle->m->connect(conn_handle, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
  2292. if (ret == FAIL) {
  2293. if (self_alloced) {
  2294. /*
  2295. We have alloced, thus there are no references to this
  2296. object - we are free to kill it!
  2297. */
  2298. conn_handle->m->dtor(conn_handle);
  2299. }
  2300. DBG_RETURN(NULL);
  2301. }
  2302. DBG_RETURN(conn_handle);
  2303. }
  2304. /* }}} */
  2305. /* {{{ mysqlnd_connection_init */
  2306. PHPAPI MYSQLND *
  2307. mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory)
  2308. {
  2309. MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
  2310. MYSQLND * ret;
  2311. DBG_ENTER("mysqlnd_connection_init");
  2312. ret = factory->get_connection(factory, persistent);
  2313. if (ret && ret->data) {
  2314. ret->data->m->negotiate_client_api_capabilities(ret->data, client_flags);
  2315. }
  2316. DBG_RETURN(ret);
  2317. }
  2318. /* }}} */
  2319. /*
  2320. * Local variables:
  2321. * tab-width: 4
  2322. * c-basic-offset: 4
  2323. * End:
  2324. * vim600: noet sw=4 ts=4 fdm=marker
  2325. * vim<600: noet sw=4 ts=4
  2326. */