mysqlnd_result.c 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2006-2016 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@mysql.com> |
  16. | Ulf Wendel <uwendel@mysql.com> |
  17. | Georg Richter <georg@mysql.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /* $Id$ */
  21. #include "php.h"
  22. #include "mysqlnd.h"
  23. #include "mysqlnd_wireprotocol.h"
  24. #include "mysqlnd_block_alloc.h"
  25. #include "mysqlnd_priv.h"
  26. #include "mysqlnd_result.h"
  27. #include "mysqlnd_result_meta.h"
  28. #include "mysqlnd_statistics.h"
  29. #include "mysqlnd_debug.h"
  30. #include "mysqlnd_ext_plugin.h"
  31. #define MYSQLND_SILENT
  32. /* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */
  33. static enum_func_status
  34. MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
  35. MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC)
  36. {
  37. unsigned int i;
  38. enum_func_status ret = PASS;
  39. const unsigned int field_count = meta->field_count;
  40. const uint64_t row_count = result->row_count;
  41. enum_func_status rc;
  42. zval **data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data;
  43. zval **data_cursor = data_begin;
  44. DBG_ENTER("mysqlnd_result_buffered_zval::initialize_result_set_rest");
  45. if (!data_cursor || row_count == result->initialized_rows) {
  46. DBG_RETURN(ret);
  47. }
  48. while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
  49. if (NULL == data_cursor[0]) {
  50. rc = result->m.row_decoder(result->row_buffers[(data_cursor - data_begin) / field_count],
  51. data_cursor,
  52. field_count,
  53. meta->fields,
  54. int_and_float_native,
  55. stats TSRMLS_CC);
  56. if (rc != PASS) {
  57. ret = FAIL;
  58. break;
  59. }
  60. result->initialized_rows++;
  61. for (i = 0; i < field_count; i++) {
  62. /*
  63. NULL fields are 0 length, 0 is not more than 0
  64. String of zero size, definitely can't be the next max_length.
  65. Thus for NULL and zero-length we are quite efficient.
  66. */
  67. if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) {
  68. unsigned long len = Z_STRLEN_P(data_cursor[i]);
  69. if (meta->fields[i].max_length < len) {
  70. meta->fields[i].max_length = len;
  71. }
  72. }
  73. }
  74. }
  75. data_cursor += field_count;
  76. }
  77. DBG_RETURN(ret);
  78. }
  79. /* }}} */
  80. /* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */
  81. static enum_func_status
  82. MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
  83. MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC)
  84. {
  85. unsigned int i;
  86. enum_func_status ret = PASS;
  87. const unsigned int field_count = meta->field_count;
  88. const uint64_t row_count = result->row_count;
  89. enum_func_status rc;
  90. DBG_ENTER("mysqlnd_result_buffered_c::initialize_result_set_rest");
  91. if (result->initialized_rows < row_count) {
  92. zend_uchar * initialized = ((MYSQLND_RES_BUFFERED_C *) result)->initialized;
  93. zval ** current_row = mnd_emalloc(field_count * sizeof(zval *));
  94. if (!current_row) {
  95. DBG_RETURN(FAIL);
  96. }
  97. for (i = 0; i < result->row_count; i++) {
  98. /* (i / 8) & the_bit_for_i*/
  99. if (initialized[i >> 3] & (1 << (i & 7))) {
  100. continue;
  101. }
  102. rc = result->m.row_decoder(result->row_buffers[i], current_row, field_count, meta->fields, int_and_float_native, stats TSRMLS_CC);
  103. if (rc != PASS) {
  104. ret = FAIL;
  105. break;
  106. }
  107. result->initialized_rows++;
  108. initialized[i >> 3] |= (1 << (i & 7));
  109. for (i = 0; i < field_count; i++) {
  110. /*
  111. NULL fields are 0 length, 0 is not more than 0
  112. String of zero size, definitely can't be the next max_length.
  113. Thus for NULL and zero-length we are quite efficient.
  114. */
  115. if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
  116. unsigned long len = Z_STRLEN_P(current_row[i]);
  117. if (meta->fields[i].max_length < len) {
  118. meta->fields[i].max_length = len;
  119. }
  120. }
  121. zval_ptr_dtor(&current_row[i]);
  122. }
  123. }
  124. mnd_efree(current_row);
  125. }
  126. DBG_RETURN(ret);
  127. }
  128. /* }}} */
  129. /* {{{ mysqlnd_rset_zval_ptr_dtor */
  130. static void
  131. mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC)
  132. {
  133. DBG_ENTER("mysqlnd_rset_zval_ptr_dtor");
  134. DBG_INF_FMT("type=%u", type);
  135. if (!zv || !*zv) {
  136. *copy_ctor_called = FALSE;
  137. DBG_ERR_FMT("zv was NULL");
  138. DBG_VOID_RETURN;
  139. }
  140. /*
  141. This zval is not from the cache block.
  142. Thus the refcount is -1 than of a zval from the cache,
  143. because the zvals from the cache are owned by it.
  144. */
  145. if (type == MYSQLND_RES_PS_BUF || type == MYSQLND_RES_PS_UNBUF) {
  146. *copy_ctor_called = FALSE;
  147. ; /* do nothing, zval_ptr_dtor will do the job*/
  148. } else if (Z_REFCOUNT_PP(zv) > 1) {
  149. /*
  150. Not a prepared statement, then we have to
  151. call copy_ctor and then zval_ptr_dtor()
  152. */
  153. if (Z_TYPE_PP(zv) == IS_STRING) {
  154. zval_copy_ctor(*zv);
  155. }
  156. *copy_ctor_called = TRUE;
  157. } else {
  158. /*
  159. noone but us point to this, so we can safely ZVAL_NULL the zval,
  160. so Zend does not try to free what the zval points to - which is
  161. in result set buffers
  162. */
  163. *copy_ctor_called = FALSE;
  164. if (Z_TYPE_PP(zv) == IS_STRING) {
  165. ZVAL_NULL(*zv);
  166. }
  167. }
  168. DBG_INF_FMT("call the dtor on zval with refc %u", Z_REFCOUNT_PP(zv));
  169. zval_ptr_dtor(zv);
  170. DBG_VOID_RETURN;
  171. }
  172. /* }}} */
  173. /* {{{ mysqlnd_result_unbuffered::free_last_data */
  174. static void
  175. MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED * unbuf, MYSQLND_STATS * const global_stats TSRMLS_DC)
  176. {
  177. DBG_ENTER("mysqlnd_res::unbuffered_free_last_data");
  178. if (!unbuf) {
  179. DBG_VOID_RETURN;
  180. }
  181. DBG_INF_FMT("field_count=%u", unbuf->field_count);
  182. if (unbuf->last_row_data) {
  183. unsigned int i, ctor_called_count = 0;
  184. zend_bool copy_ctor_called;
  185. for (i = 0; i < unbuf->field_count; i++) {
  186. mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), unbuf->ps ? MYSQLND_RES_PS_UNBUF : MYSQLND_RES_NORMAL, &copy_ctor_called TSRMLS_CC);
  187. if (copy_ctor_called) {
  188. ++ctor_called_count;
  189. }
  190. }
  191. DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count);
  192. /* By using value3 macros we hold a mutex only once, there is no value2 */
  193. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats,
  194. STAT_COPY_ON_WRITE_PERFORMED,
  195. ctor_called_count,
  196. STAT_COPY_ON_WRITE_SAVED,
  197. unbuf->field_count - ctor_called_count);
  198. /* Free last row's zvals */
  199. mnd_efree(unbuf->last_row_data);
  200. unbuf->last_row_data = NULL;
  201. }
  202. if (unbuf->last_row_buffer) {
  203. DBG_INF("Freeing last row buffer");
  204. /* Nothing points to this buffer now, free it */
  205. unbuf->last_row_buffer->free_chunk(unbuf->last_row_buffer TSRMLS_CC);
  206. unbuf->last_row_buffer = NULL;
  207. }
  208. DBG_VOID_RETURN;
  209. }
  210. /* }}} */
  211. /* {{{ mysqlnd_result_unbuffered::free_result */
  212. static void
  213. MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats TSRMLS_DC)
  214. {
  215. DBG_ENTER("mysqlnd_result_unbuffered, free_result");
  216. result->m.free_last_data(result, global_stats TSRMLS_CC);
  217. if (result->lengths) {
  218. mnd_pefree(result->lengths, result->persistent);
  219. result->lengths = NULL;
  220. }
  221. /* must be free before because references the memory pool */
  222. if (result->row_packet) {
  223. PACKET_FREE(result->row_packet);
  224. result->row_packet = NULL;
  225. }
  226. if (result->result_set_memory_pool) {
  227. mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
  228. result->result_set_memory_pool = NULL;
  229. }
  230. mnd_pefree(result, result->persistent);
  231. DBG_VOID_RETURN;
  232. }
  233. /* }}} */
  234. /* {{{ mysqlnd_result_buffered_zval::free_result */
  235. static void
  236. MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_ZVAL * const set TSRMLS_DC)
  237. {
  238. zval ** data = set->data;
  239. DBG_ENTER("mysqlnd_result_buffered_zval::free_result");
  240. set->data = NULL; /* prevent double free if following loop is interrupted */
  241. if (data) {
  242. unsigned int copy_on_write_performed = 0;
  243. unsigned int copy_on_write_saved = 0;
  244. unsigned int field_count = set->field_count;
  245. int64_t row;
  246. for (row = set->row_count - 1; row >= 0; row--) {
  247. zval **current_row = data + row * field_count;
  248. int64_t col;
  249. if (current_row != NULL) {
  250. for (col = field_count - 1; col >= 0; --col) {
  251. if (current_row[col]) {
  252. zend_bool copy_ctor_called;
  253. mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), set->ps? MYSQLND_RES_PS_BUF : MYSQLND_RES_NORMAL, &copy_ctor_called TSRMLS_CC);
  254. if (copy_ctor_called) {
  255. ++copy_on_write_performed;
  256. } else {
  257. ++copy_on_write_saved;
  258. }
  259. }
  260. }
  261. }
  262. }
  263. MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed,
  264. STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved);
  265. mnd_efree(data);
  266. }
  267. set->data_cursor = NULL;
  268. DBG_VOID_RETURN;
  269. }
  270. /* }}} */
  271. /* {{{ mysqlnd_result_buffered_c::free_result */
  272. static void
  273. MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)(MYSQLND_RES_BUFFERED_C * const set TSRMLS_DC)
  274. {
  275. DBG_ENTER("mysqlnd_result_buffered_c::free_result");
  276. mnd_pefree(set->initialized, set->persistent);
  277. set->initialized = NULL;
  278. DBG_VOID_RETURN;
  279. }
  280. /* }}} */
  281. /* {{{ mysqlnd_result_buffered::free_result */
  282. static void
  283. MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC)
  284. {
  285. int64_t row;
  286. DBG_ENTER("mysqlnd_result_buffered::free_result");
  287. DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
  288. if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
  289. MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set TSRMLS_CC);
  290. } if (set->type == MYSQLND_BUFFERED_TYPE_C) {
  291. MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set TSRMLS_CC);
  292. }
  293. for (row = set->row_count - 1; row >= 0; row--) {
  294. MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
  295. current_buffer->free_chunk(current_buffer TSRMLS_CC);
  296. }
  297. if (set->lengths) {
  298. mnd_pefree(set->lengths, set->persistent);
  299. set->lengths = NULL;
  300. }
  301. if (set->row_buffers) {
  302. mnd_pefree(set->row_buffers, 0);
  303. set->row_buffers = NULL;
  304. }
  305. if (set->result_set_memory_pool) {
  306. mysqlnd_mempool_destroy(set->result_set_memory_pool TSRMLS_CC);
  307. set->result_set_memory_pool = NULL;
  308. }
  309. set->row_count = 0;
  310. mnd_pefree(set, set->persistent);
  311. DBG_VOID_RETURN;
  312. }
  313. /* }}} */
  314. /* {{{ mysqlnd_res::free_result_buffers */
  315. static void
  316. MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC)
  317. {
  318. DBG_ENTER("mysqlnd_res::free_result_buffers");
  319. DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
  320. if (result->unbuf) {
  321. result->unbuf->m.free_result(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
  322. result->unbuf = NULL;
  323. } else if (result->stored_data) {
  324. result->stored_data->m.free_result(result->stored_data TSRMLS_CC);
  325. result->stored_data = NULL;
  326. }
  327. DBG_VOID_RETURN;
  328. }
  329. /* }}} */
  330. /* {{{ mysqlnd_res::free_result_contents_internal */
  331. static
  332. void MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal)(MYSQLND_RES * result TSRMLS_DC)
  333. {
  334. DBG_ENTER("mysqlnd_res::free_result_contents_internal");
  335. result->m.free_result_buffers(result TSRMLS_CC);
  336. if (result->meta) {
  337. result->meta->m->free_metadata(result->meta TSRMLS_CC);
  338. result->meta = NULL;
  339. }
  340. DBG_VOID_RETURN;
  341. }
  342. /* }}} */
  343. /* {{{ mysqlnd_res::free_result_internal */
  344. static
  345. void MYSQLND_METHOD(mysqlnd_res, free_result_internal)(MYSQLND_RES * result TSRMLS_DC)
  346. {
  347. DBG_ENTER("mysqlnd_res::free_result_internal");
  348. result->m.skip_result(result TSRMLS_CC);
  349. result->m.free_result_contents(result TSRMLS_CC);
  350. if (result->conn) {
  351. result->conn->m->free_reference(result->conn TSRMLS_CC);
  352. result->conn = NULL;
  353. }
  354. mnd_pefree(result, result->persistent);
  355. DBG_VOID_RETURN;
  356. }
  357. /* }}} */
  358. /* {{{ mysqlnd_res::read_result_metadata */
  359. static enum_func_status
  360. MYSQLND_METHOD(mysqlnd_res, read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC)
  361. {
  362. DBG_ENTER("mysqlnd_res::read_result_metadata");
  363. /*
  364. Make it safe to call it repeatedly for PS -
  365. better free and allocate a new because the number of field might change
  366. (select *) with altered table. Also for statements which skip the PS
  367. infrastructure!
  368. */
  369. if (result->meta) {
  370. result->meta->m->free_metadata(result->meta TSRMLS_CC);
  371. result->meta = NULL;
  372. }
  373. result->meta = result->m.result_meta_init(result->field_count, result->persistent TSRMLS_CC);
  374. if (!result->meta) {
  375. SET_OOM_ERROR(*conn->error_info);
  376. DBG_RETURN(FAIL);
  377. }
  378. /* 1. Read all fields metadata */
  379. /* It's safe to reread without freeing */
  380. if (FAIL == result->meta->m->read_metadata(result->meta, conn TSRMLS_CC)) {
  381. result->m.free_result_contents(result TSRMLS_CC);
  382. DBG_RETURN(FAIL);
  383. }
  384. /* COM_FIELD_LIST is broken and has premature EOF, thus we need to hack here and in mysqlnd_res_meta.c */
  385. result->field_count = result->meta->field_count;
  386. /*
  387. 2. Follows an EOF packet, which the client of mysqlnd_read_result_metadata()
  388. should consume.
  389. 3. If there is a result set, it follows. The last packet will have 'eof' set
  390. If PS, then no result set follows.
  391. */
  392. DBG_RETURN(PASS);
  393. }
  394. /* }}} */
  395. /* {{{ mysqlnd_query_read_result_set_header */
  396. enum_func_status
  397. mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s TSRMLS_DC)
  398. {
  399. MYSQLND_STMT_DATA * stmt = s ? s->data:NULL;
  400. enum_func_status ret;
  401. MYSQLND_PACKET_RSET_HEADER * rset_header = NULL;
  402. MYSQLND_PACKET_EOF * fields_eof = NULL;
  403. DBG_ENTER("mysqlnd_query_read_result_set_header");
  404. DBG_INF_FMT("stmt=%lu", stmt? stmt->stmt_id:0);
  405. ret = FAIL;
  406. do {
  407. rset_header = conn->protocol->m.get_rset_header_packet(conn->protocol, FALSE TSRMLS_CC);
  408. if (!rset_header) {
  409. SET_OOM_ERROR(*conn->error_info);
  410. ret = FAIL;
  411. break;
  412. }
  413. SET_ERROR_AFF_ROWS(conn);
  414. if (FAIL == (ret = PACKET_READ(rset_header, conn))) {
  415. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error reading result set's header");
  416. break;
  417. }
  418. if (rset_header->error_info.error_no) {
  419. /*
  420. Cover a protocol design error: error packet does not
  421. contain the server status. Therefore, the client has no way
  422. to find out whether there are more result sets of
  423. a multiple-result-set statement pending. Luckily, in 5.0 an
  424. error always aborts execution of a statement, wherever it is
  425. a multi-statement or a stored procedure, so it should be
  426. safe to unconditionally turn off the flag here.
  427. */
  428. conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
  429. /*
  430. This will copy the error code and the messages, as they
  431. are buffers in the struct
  432. */
  433. COPY_CLIENT_ERROR(*conn->error_info, rset_header->error_info);
  434. ret = FAIL;
  435. DBG_ERR_FMT("error=%s", rset_header->error_info.error);
  436. /* Return back from CONN_QUERY_SENT */
  437. CONN_SET_STATE(conn, CONN_READY);
  438. break;
  439. }
  440. conn->error_info->error_no = 0;
  441. switch (rset_header->field_count) {
  442. case MYSQLND_NULL_LENGTH: { /* LOAD DATA LOCAL INFILE */
  443. zend_bool is_warning;
  444. DBG_INF("LOAD DATA");
  445. conn->last_query_type = QUERY_LOAD_LOCAL;
  446. conn->field_count = 0; /* overwrite previous value, or the last value could be used and lead to bug#53503 */
  447. CONN_SET_STATE(conn, CONN_SENDING_LOAD_DATA);
  448. ret = mysqlnd_handle_local_infile(conn, rset_header->info_or_local_file, &is_warning TSRMLS_CC);
  449. CONN_SET_STATE(conn, (ret == PASS || is_warning == TRUE)? CONN_READY:CONN_QUIT_SENT);
  450. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
  451. break;
  452. }
  453. case 0: /* UPSERT */
  454. DBG_INF("UPSERT");
  455. conn->last_query_type = QUERY_UPSERT;
  456. conn->field_count = rset_header->field_count;
  457. memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
  458. conn->upsert_status->warning_count = rset_header->warning_count;
  459. conn->upsert_status->server_status = rset_header->server_status;
  460. conn->upsert_status->affected_rows = rset_header->affected_rows;
  461. conn->upsert_status->last_insert_id = rset_header->last_insert_id;
  462. SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
  463. rset_header->info_or_local_file, rset_header->info_or_local_file_len,
  464. conn->persistent);
  465. /* Result set can follow UPSERT statement, check server_status */
  466. if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
  467. CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
  468. } else {
  469. CONN_SET_STATE(conn, CONN_READY);
  470. }
  471. ret = PASS;
  472. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_NON_RSET_QUERY);
  473. break;
  474. default: do { /* Result set */
  475. MYSQLND_RES * result;
  476. enum_mysqlnd_collected_stats statistic = STAT_LAST;
  477. DBG_INF("Result set pending");
  478. SET_EMPTY_MESSAGE(conn->last_message, conn->last_message_len, conn->persistent);
  479. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_RSET_QUERY);
  480. memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
  481. /* restore after zeroing */
  482. SET_ERROR_AFF_ROWS(conn);
  483. conn->last_query_type = QUERY_SELECT;
  484. CONN_SET_STATE(conn, CONN_FETCHING_DATA);
  485. /* PS has already allocated it */
  486. conn->field_count = rset_header->field_count;
  487. if (!stmt) {
  488. result = conn->current_result = conn->m->result_init(rset_header->field_count, conn->persistent TSRMLS_CC);
  489. } else {
  490. if (!stmt->result) {
  491. DBG_INF("This is 'SHOW'/'EXPLAIN'-like query.");
  492. /*
  493. This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
  494. prepared statements can't send result set metadata for these queries
  495. on prepare stage. Read it now.
  496. */
  497. result = stmt->result = conn->m->result_init(rset_header->field_count, stmt->persistent TSRMLS_CC);
  498. } else {
  499. /*
  500. Update result set metadata if it for some reason changed between
  501. prepare and execute, i.e.:
  502. - in case of 'SELECT ?' we don't know column type unless data was
  503. supplied to mysql_stmt_execute, so updated column type is sent
  504. now.
  505. - if data dictionary changed between prepare and execute, for
  506. example a table used in the query was altered.
  507. Note, that now (4.1.3) we always send metadata in reply to
  508. COM_STMT_EXECUTE (even if it is not necessary), so either this or
  509. previous branch always works.
  510. */
  511. }
  512. result = stmt->result;
  513. }
  514. if (!result) {
  515. SET_OOM_ERROR(*conn->error_info);
  516. ret = FAIL;
  517. break;
  518. }
  519. if (FAIL == (ret = result->m.read_result_metadata(result, conn TSRMLS_CC))) {
  520. /* For PS, we leave them in Prepared state */
  521. if (!stmt && conn->current_result) {
  522. mnd_efree(conn->current_result);
  523. conn->current_result = NULL;
  524. }
  525. DBG_ERR("Error occurred while reading metadata");
  526. break;
  527. }
  528. /* Check for SERVER_STATUS_MORE_RESULTS if needed */
  529. fields_eof = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
  530. if (!fields_eof) {
  531. SET_OOM_ERROR(*conn->error_info);
  532. ret = FAIL;
  533. break;
  534. }
  535. if (FAIL == (ret = PACKET_READ(fields_eof, conn))) {
  536. DBG_ERR("Error occurred while reading the EOF packet");
  537. result->m.free_result_contents(result TSRMLS_CC);
  538. mnd_efree(result);
  539. if (!stmt) {
  540. conn->current_result = NULL;
  541. } else {
  542. stmt->result = NULL;
  543. memset(stmt, 0, sizeof(*stmt));
  544. stmt->state = MYSQLND_STMT_INITTED;
  545. }
  546. } else {
  547. unsigned int to_log = MYSQLND_G(log_mask);
  548. to_log &= fields_eof->server_status;
  549. DBG_INF_FMT("warnings=%u server_status=%u", fields_eof->warning_count, fields_eof->server_status);
  550. conn->upsert_status->warning_count = fields_eof->warning_count;
  551. /*
  552. If SERVER_MORE_RESULTS_EXISTS is set then this is either MULTI_QUERY or a CALL()
  553. The first packet after sending the query/com_execute has the bit set only
  554. in this cases. Not sure why it's a needed but it marks that the whole stream
  555. will include many result sets. What actually matters are the bits set at the end
  556. of every result set (the EOF packet).
  557. */
  558. conn->upsert_status->server_status = fields_eof->server_status;
  559. if (fields_eof->server_status & SERVER_QUERY_NO_GOOD_INDEX_USED) {
  560. statistic = STAT_BAD_INDEX_USED;
  561. } else if (fields_eof->server_status & SERVER_QUERY_NO_INDEX_USED) {
  562. statistic = STAT_NO_INDEX_USED;
  563. } else if (fields_eof->server_status & SERVER_QUERY_WAS_SLOW) {
  564. statistic = STAT_QUERY_WAS_SLOW;
  565. }
  566. if (to_log) {
  567. #if A0
  568. char *backtrace = mysqlnd_get_backtrace(TSRMLS_C);
  569. php_log_err(backtrace TSRMLS_CC);
  570. efree(backtrace);
  571. #endif
  572. }
  573. MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
  574. }
  575. } while (0);
  576. PACKET_FREE(fields_eof);
  577. break; /* switch break */
  578. }
  579. } while (0);
  580. PACKET_FREE(rset_header);
  581. DBG_INF(ret == PASS? "PASS":"FAIL");
  582. DBG_RETURN(ret);
  583. }
  584. /* }}} */
  585. /* {{{ mysqlnd_result_buffered::fetch_lengths */
  586. /*
  587. Do lazy initialization for buffered results. As PHP strings have
  588. length inside, this function makes not much sense in the context
  589. of PHP, to be called as separate function. But let's have it for
  590. completeness.
  591. */
  592. static unsigned long *
  593. MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
  594. {
  595. const MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
  596. /*
  597. If:
  598. - unbuffered result
  599. - first row has not been read
  600. - last_row has been read
  601. */
  602. DBG_ENTER("mysqlnd_result_buffered_zval::fetch_lengths");
  603. if (set->data_cursor == NULL ||
  604. set->data_cursor == set->data ||
  605. ((set->data_cursor - set->data) > (result->row_count * result->field_count) ))
  606. {
  607. DBG_INF("EOF");
  608. DBG_RETURN(NULL);/* No rows or no more rows */
  609. }
  610. DBG_INF("non NULL");
  611. DBG_RETURN(result->lengths);
  612. }
  613. /* }}} */
  614. /* {{{ mysqlnd_result_buffered_c::fetch_lengths */
  615. /*
  616. Do lazy initialization for buffered results. As PHP strings have
  617. length inside, this function makes not much sense in the context
  618. of PHP, to be called as separate function. But let's have it for
  619. completeness.
  620. */
  621. static unsigned long *
  622. MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
  623. {
  624. const MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
  625. DBG_ENTER("mysqlnd_result_buffered_c::fetch_lengths");
  626. if (set->current_row > set->row_count || set->current_row == 0) {
  627. DBG_INF("EOF");
  628. DBG_RETURN(NULL); /* No more rows, or no fetched row */
  629. }
  630. DBG_INF("non NULL");
  631. DBG_RETURN(result->lengths);
  632. }
  633. /* }}} */
  634. /* {{{ mysqlnd_result_unbuffered::fetch_lengths */
  635. static unsigned long *
  636. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC)
  637. {
  638. /* simulate output of libmysql */
  639. return (result->last_row_data || result->eof_reached)? result->lengths : NULL;
  640. }
  641. /* }}} */
  642. /* {{{ mysqlnd_res::fetch_lengths */
  643. static unsigned long *
  644. MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC)
  645. {
  646. unsigned long * ret;
  647. DBG_ENTER("mysqlnd_res::fetch_lengths");
  648. ret = result->stored_data && result->stored_data->m.fetch_lengths ?
  649. result->stored_data->m.fetch_lengths(result->stored_data TSRMLS_CC) :
  650. (result->unbuf && result->unbuf->m.fetch_lengths ?
  651. result->unbuf->m.fetch_lengths(result->unbuf TSRMLS_CC) :
  652. NULL
  653. );
  654. DBG_RETURN(ret);
  655. }
  656. /* }}} */
  657. /* {{{ mysqlnd_result_unbuffered::fetch_row_c */
  658. static enum_func_status
  659. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
  660. {
  661. enum_func_status ret;
  662. MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param;
  663. MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
  664. const MYSQLND_RES_METADATA * const meta = result->meta;
  665. DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c");
  666. *fetched_anything = FALSE;
  667. if (result->unbuf->eof_reached) {
  668. /* No more rows obviously */
  669. DBG_RETURN(PASS);
  670. }
  671. if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
  672. SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
  673. DBG_RETURN(FAIL);
  674. }
  675. if (!row_packet) {
  676. /* Not fully initialized object that is being cleaned up */
  677. DBG_RETURN(FAIL);
  678. }
  679. /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
  680. row_packet->skip_extraction = FALSE;
  681. /*
  682. If we skip rows (row == NULL) we have to
  683. result->m.unbuffered_free_last_data() before it. The function returns always true.
  684. */
  685. if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
  686. result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
  687. result->unbuf->last_row_data = row_packet->fields;
  688. result->unbuf->last_row_buffer = row_packet->row_buffer;
  689. row_packet->fields = NULL;
  690. row_packet->row_buffer = NULL;
  691. MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
  692. if (!row_packet->skip_extraction) {
  693. unsigned int i, field_count = meta->field_count;
  694. enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
  695. result->unbuf->last_row_data,
  696. field_count,
  697. row_packet->fields_metadata,
  698. result->conn->options->int_and_float_native,
  699. result->conn->stats TSRMLS_CC);
  700. if (PASS != rc) {
  701. DBG_RETURN(FAIL);
  702. }
  703. {
  704. *row = mnd_malloc(field_count * sizeof(char *));
  705. if (*row) {
  706. MYSQLND_FIELD * field = meta->fields;
  707. unsigned long * lengths = result->unbuf->lengths;
  708. for (i = 0; i < field_count; i++, field++) {
  709. zval * data = result->unbuf->last_row_data[i];
  710. unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
  711. /* BEGIN difference between normal normal fetch and _c */
  712. if (Z_TYPE_P(data) != IS_NULL) {
  713. convert_to_string(data);
  714. (*row)[i] = Z_STRVAL_P(data);
  715. } else {
  716. (*row)[i] = NULL;
  717. }
  718. /* END difference between normal normal fetch and _c */
  719. if (lengths) {
  720. lengths[i] = len;
  721. }
  722. if (field->max_length < len) {
  723. field->max_length = len;
  724. }
  725. }
  726. } else {
  727. SET_OOM_ERROR(*result->conn->error_info);
  728. }
  729. }
  730. }
  731. result->unbuf->row_count++;
  732. *fetched_anything = TRUE;
  733. } else if (ret == FAIL) {
  734. if (row_packet->error_info.error_no) {
  735. COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
  736. DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
  737. }
  738. CONN_SET_STATE(result->conn, CONN_READY);
  739. result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
  740. } else if (row_packet->eof) {
  741. /* Mark the connection as usable again */
  742. DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
  743. result->unbuf->eof_reached = TRUE;
  744. memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
  745. result->conn->upsert_status->warning_count = row_packet->warning_count;
  746. result->conn->upsert_status->server_status = row_packet->server_status;
  747. /*
  748. result->row_packet will be cleaned when
  749. destroying the result object
  750. */
  751. if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
  752. CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
  753. } else {
  754. CONN_SET_STATE(result->conn, CONN_READY);
  755. }
  756. result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
  757. }
  758. DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
  759. DBG_RETURN(PASS);
  760. }
  761. /* }}} */
  762. /* {{{ mysqlnd_result_unbuffered::fetch_row */
  763. static enum_func_status
  764. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
  765. {
  766. enum_func_status ret;
  767. zval *row = (zval *) param;
  768. MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
  769. const MYSQLND_RES_METADATA * const meta = result->meta;
  770. DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
  771. *fetched_anything = FALSE;
  772. if (result->unbuf->eof_reached) {
  773. /* No more rows obviously */
  774. DBG_RETURN(PASS);
  775. }
  776. if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
  777. SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
  778. DBG_RETURN(FAIL);
  779. }
  780. if (!row_packet) {
  781. /* Not fully initialized object that is being cleaned up */
  782. DBG_RETURN(FAIL);
  783. }
  784. /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
  785. row_packet->skip_extraction = row? FALSE:TRUE;
  786. /*
  787. If we skip rows (row == NULL) we have to
  788. result->m.unbuffered_free_last_data() before it. The function returns always true.
  789. */
  790. if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
  791. result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
  792. result->unbuf->last_row_data = row_packet->fields;
  793. result->unbuf->last_row_buffer = row_packet->row_buffer;
  794. row_packet->fields = NULL;
  795. row_packet->row_buffer = NULL;
  796. MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
  797. if (!row_packet->skip_extraction) {
  798. unsigned int i, field_count = meta->field_count;
  799. enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
  800. result->unbuf->last_row_data,
  801. field_count,
  802. row_packet->fields_metadata,
  803. result->conn->options->int_and_float_native,
  804. result->conn->stats TSRMLS_CC);
  805. if (PASS != rc) {
  806. DBG_RETURN(FAIL);
  807. }
  808. {
  809. HashTable * row_ht = Z_ARRVAL_P(row);
  810. MYSQLND_FIELD * field = meta->fields;
  811. unsigned long * lengths = result->unbuf->lengths;
  812. for (i = 0; i < field_count; i++, field++) {
  813. zval * data = result->unbuf->last_row_data[i];
  814. unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
  815. if (flags & MYSQLND_FETCH_NUM) {
  816. Z_ADDREF_P(data);
  817. zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL);
  818. }
  819. if (flags & MYSQLND_FETCH_ASSOC) {
  820. /* zend_hash_quick_update needs length + trailing zero */
  821. /* QQ: Error handling ? */
  822. /*
  823. zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
  824. the index is a numeric and convert it to it. This however means constant
  825. hashing of the column name, which is not needed as it can be precomputed.
  826. */
  827. Z_ADDREF_P(data);
  828. if (meta->zend_hash_keys[i].is_numeric == FALSE) {
  829. zend_hash_quick_update(Z_ARRVAL_P(row),
  830. field->name,
  831. field->name_length + 1,
  832. meta->zend_hash_keys[i].key,
  833. (void *) &data, sizeof(zval *), NULL);
  834. } else {
  835. zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL);
  836. }
  837. }
  838. if (lengths) {
  839. lengths[i] = len;
  840. }
  841. if (field->max_length < len) {
  842. field->max_length = len;
  843. }
  844. }
  845. }
  846. }
  847. result->unbuf->row_count++;
  848. *fetched_anything = TRUE;
  849. } else if (ret == FAIL) {
  850. if (row_packet->error_info.error_no) {
  851. COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
  852. DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
  853. }
  854. CONN_SET_STATE(result->conn, CONN_READY);
  855. result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
  856. } else if (row_packet->eof) {
  857. /* Mark the connection as usable again */
  858. DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
  859. result->unbuf->eof_reached = TRUE;
  860. memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status));
  861. result->conn->upsert_status->warning_count = row_packet->warning_count;
  862. result->conn->upsert_status->server_status = row_packet->server_status;
  863. /*
  864. result->row_packet will be cleaned when
  865. destroying the result object
  866. */
  867. if (result->conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
  868. CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
  869. } else {
  870. CONN_SET_STATE(result->conn, CONN_READY);
  871. }
  872. result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
  873. }
  874. DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
  875. DBG_RETURN(PASS);
  876. }
  877. /* }}} */
  878. /* {{{ mysqlnd_res::use_result */
  879. static MYSQLND_RES *
  880. MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps TSRMLS_DC)
  881. {
  882. DBG_ENTER("mysqlnd_res::use_result");
  883. SET_EMPTY_ERROR(*result->conn->error_info);
  884. if (ps == FALSE) {
  885. result->type = MYSQLND_RES_NORMAL;
  886. } else {
  887. result->type = MYSQLND_RES_PS_UNBUF;
  888. }
  889. result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, ps, result->persistent TSRMLS_CC);
  890. if (!result->unbuf) {
  891. goto oom;
  892. }
  893. /*
  894. Will be freed in the mysqlnd_internal_free_result_contents() called
  895. by the resource destructor. mysqlnd_result_unbuffered::fetch_row() expects
  896. this to be not NULL.
  897. */
  898. /* FALSE = non-persistent */
  899. result->unbuf->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
  900. if (!result->unbuf->row_packet) {
  901. goto oom;
  902. }
  903. result->unbuf->row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
  904. result->unbuf->row_packet->field_count = result->field_count;
  905. result->unbuf->row_packet->binary_protocol = ps;
  906. result->unbuf->row_packet->fields_metadata = result->meta->fields;
  907. result->unbuf->row_packet->bit_fields_count = result->meta->bit_fields_count;
  908. result->unbuf->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
  909. DBG_RETURN(result);
  910. oom:
  911. SET_OOM_ERROR(*result->conn->error_info);
  912. DBG_RETURN(NULL);
  913. }
  914. /* }}} */
  915. /* {{{ mysqlnd_result_buffered::fetch_row_c */
  916. static enum_func_status
  917. MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
  918. {
  919. MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param;
  920. const MYSQLND_RES_METADATA * const meta = result->meta;
  921. unsigned int field_count = meta->field_count;
  922. enum_func_status ret = FAIL;
  923. DBG_ENTER("mysqlnd_result_buffered::fetch_row_c");
  924. if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
  925. MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
  926. /* If we haven't read everything */
  927. if (set->data_cursor &&
  928. (set->data_cursor - set->data) < (result->stored_data->row_count * field_count))
  929. {
  930. zval **current_row = set->data_cursor;
  931. unsigned int i;
  932. if (NULL == current_row[0]) {
  933. uint64_t row_num = (set->data_cursor - set->data) / field_count;
  934. enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num],
  935. current_row,
  936. field_count,
  937. meta->fields,
  938. result->conn->options->int_and_float_native,
  939. result->conn->stats TSRMLS_CC);
  940. if (rc != PASS) {
  941. DBG_RETURN(FAIL);
  942. }
  943. set->initialized_rows++;
  944. for (i = 0; i < field_count; i++) {
  945. /*
  946. NULL fields are 0 length, 0 is not more than 0
  947. String of zero size, definitely can't be the next max_length.
  948. Thus for NULL and zero-length we are quite efficient.
  949. */
  950. if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
  951. unsigned long len = Z_STRLEN_P(current_row[i]);
  952. if (meta->fields[i].max_length < len) {
  953. meta->fields[i].max_length = len;
  954. }
  955. }
  956. }
  957. }
  958. /* BEGIN difference between normal normal fetch and _c */
  959. /* there is no conn handle in this function thus we can't set OOM in error_info */
  960. *row = mnd_malloc(field_count * sizeof(char *));
  961. if (*row) {
  962. for (i = 0; i < field_count; i++) {
  963. zval * data = current_row[i];
  964. set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
  965. if (Z_TYPE_P(data) != IS_NULL) {
  966. convert_to_string(data);
  967. (*row)[i] = Z_STRVAL_P(data);
  968. } else {
  969. (*row)[i] = NULL;
  970. }
  971. }
  972. set->data_cursor += field_count;
  973. MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
  974. } else {
  975. SET_OOM_ERROR(*result->conn->error_info);
  976. }
  977. /* END difference between normal normal fetch and _c */
  978. *fetched_anything = *row? TRUE:FALSE;
  979. ret = *row? PASS:FAIL;
  980. } else {
  981. set->data_cursor = NULL;
  982. DBG_INF("EOF reached");
  983. *fetched_anything = FALSE;
  984. ret = PASS;
  985. }
  986. } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) {
  987. /*
  988. We don't support _C with pdo because it uses the data in a different way - just references it.
  989. We will either leak or give nirvana pointers
  990. */
  991. *fetched_anything = FALSE;
  992. DBG_RETURN(FAIL);
  993. }
  994. DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
  995. DBG_RETURN(ret);
  996. }
  997. /* }}} */
  998. /* {{{ mysqlnd_result_buffered_zval::fetch_row */
  999. static enum_func_status
  1000. MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
  1001. {
  1002. zval * row = (zval *) param;
  1003. const MYSQLND_RES_METADATA * const meta = result->meta;
  1004. unsigned int field_count = meta->field_count;
  1005. enum_func_status ret = FAIL;
  1006. MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
  1007. DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row");
  1008. /* If we haven't read everything */
  1009. if (set->data_cursor &&
  1010. (set->data_cursor - set->data) < (set->row_count * field_count))
  1011. {
  1012. unsigned int i;
  1013. zval **current_row = set->data_cursor;
  1014. if (NULL == current_row[0]) {
  1015. uint64_t row_num = (set->data_cursor - set->data) / field_count;
  1016. enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num],
  1017. current_row,
  1018. field_count,
  1019. meta->fields,
  1020. result->conn->options->int_and_float_native,
  1021. result->conn->stats TSRMLS_CC);
  1022. if (rc != PASS) {
  1023. DBG_RETURN(FAIL);
  1024. }
  1025. set->initialized_rows++;
  1026. for (i = 0; i < field_count; i++) {
  1027. /*
  1028. NULL fields are 0 length, 0 is not more than 0
  1029. String of zero size, definitely can't be the next max_length.
  1030. Thus for NULL and zero-length we are quite efficient.
  1031. */
  1032. if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
  1033. unsigned long len = Z_STRLEN_P(current_row[i]);
  1034. if (meta->fields[i].max_length < len) {
  1035. meta->fields[i].max_length = len;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. for (i = 0; i < field_count; i++) {
  1041. zval * data = current_row[i];
  1042. set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
  1043. if (flags & MYSQLND_FETCH_NUM) {
  1044. Z_ADDREF_P(data);
  1045. zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
  1046. }
  1047. if (flags & MYSQLND_FETCH_ASSOC) {
  1048. /* zend_hash_quick_update needs length + trailing zero */
  1049. /* QQ: Error handling ? */
  1050. /*
  1051. zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
  1052. the index is a numeric and convert it to it. This however means constant
  1053. hashing of the column name, which is not needed as it can be precomputed.
  1054. */
  1055. Z_ADDREF_P(data);
  1056. if (meta->zend_hash_keys[i].is_numeric == FALSE) {
  1057. zend_hash_quick_update(Z_ARRVAL_P(row),
  1058. meta->fields[i].name,
  1059. meta->fields[i].name_length + 1,
  1060. meta->zend_hash_keys[i].key,
  1061. (void *) &data, sizeof(zval *), NULL);
  1062. } else {
  1063. zend_hash_index_update(Z_ARRVAL_P(row),
  1064. meta->zend_hash_keys[i].key,
  1065. (void *) &data, sizeof(zval *), NULL);
  1066. }
  1067. }
  1068. }
  1069. set->data_cursor += field_count;
  1070. MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
  1071. *fetched_anything = TRUE;
  1072. ret = PASS;
  1073. } else {
  1074. set->data_cursor = NULL;
  1075. DBG_INF("EOF reached");
  1076. *fetched_anything = FALSE;
  1077. ret = PASS;
  1078. }
  1079. DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
  1080. DBG_RETURN(ret);
  1081. }
  1082. /* }}} */
  1083. /* {{{ mysqlnd_result_buffered_c::fetch_row */
  1084. static enum_func_status
  1085. MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
  1086. {
  1087. zval * row = (zval *) param;
  1088. const MYSQLND_RES_METADATA * const meta = result->meta;
  1089. unsigned int field_count = meta->field_count;
  1090. enum_func_status ret = FAIL;
  1091. MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
  1092. DBG_ENTER("mysqlnd_result_buffered_c::fetch_row");
  1093. /* If we haven't read everything */
  1094. if (set->current_row < set->row_count) {
  1095. zval **current_row;
  1096. enum_func_status rc;
  1097. unsigned int i;
  1098. current_row = mnd_emalloc(field_count * sizeof(zval *));
  1099. if (!current_row) {
  1100. SET_OOM_ERROR(*result->conn->error_info);
  1101. DBG_RETURN(FAIL);
  1102. }
  1103. rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[set->current_row],
  1104. current_row,
  1105. field_count,
  1106. meta->fields,
  1107. result->conn->options->int_and_float_native,
  1108. result->conn->stats TSRMLS_CC);
  1109. if (rc != PASS) {
  1110. DBG_RETURN(FAIL);
  1111. }
  1112. if (!(set->initialized[set->current_row >> 3] & (1 << (set->current_row & 7)))) {
  1113. set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */
  1114. set->initialized_rows++;
  1115. for (i = 0; i < field_count; i++) {
  1116. /*
  1117. NULL fields are 0 length, 0 is not more than 0
  1118. String of zero size, definitely can't be the next max_length.
  1119. Thus for NULL and zero-length we are quite efficient.
  1120. */
  1121. if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
  1122. unsigned long len = Z_STRLEN_P(current_row[i]);
  1123. if (meta->fields[i].max_length < len) {
  1124. meta->fields[i].max_length = len;
  1125. }
  1126. }
  1127. }
  1128. }
  1129. for (i = 0; i < field_count; i++) {
  1130. zval * data = current_row[i];
  1131. set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
  1132. if (flags & MYSQLND_FETCH_NUM) {
  1133. Z_ADDREF_P(data);
  1134. zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
  1135. }
  1136. if (flags & MYSQLND_FETCH_ASSOC) {
  1137. /* zend_hash_quick_update needs length + trailing zero */
  1138. /* QQ: Error handling ? */
  1139. /*
  1140. zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
  1141. the index is a numeric and convert it to it. This however means constant
  1142. hashing of the column name, which is not needed as it can be precomputed.
  1143. */
  1144. Z_ADDREF_P(data);
  1145. if (meta->zend_hash_keys[i].is_numeric == FALSE) {
  1146. zend_hash_quick_update(Z_ARRVAL_P(row),
  1147. meta->fields[i].name,
  1148. meta->fields[i].name_length + 1,
  1149. meta->zend_hash_keys[i].key,
  1150. (void *) &data, sizeof(zval *), NULL);
  1151. } else {
  1152. zend_hash_index_update(Z_ARRVAL_P(row),
  1153. meta->zend_hash_keys[i].key,
  1154. (void *) &data, sizeof(zval *), NULL);
  1155. }
  1156. }
  1157. /*
  1158. This will usually not destroy anything but decref.
  1159. However, if neither NUM nor ASSOC is set we will free memory cleanly and won't leak.
  1160. It also simplifies the handling of Z_ADDREF_P because we don't need to check if only
  1161. either NUM or ASSOC is set but not both.
  1162. */
  1163. zval_ptr_dtor(&data);
  1164. }
  1165. mnd_efree(current_row);
  1166. set->current_row++;
  1167. MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
  1168. *fetched_anything = TRUE;
  1169. ret = PASS;
  1170. } else {
  1171. if (set->current_row == set->row_count) {
  1172. set->current_row = set->row_count + 1;
  1173. }
  1174. DBG_INF_FMT("EOF reached. current_row=%llu", (unsigned long long) set->current_row);
  1175. *fetched_anything = FALSE;
  1176. ret = PASS;
  1177. }
  1178. DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
  1179. DBG_RETURN(ret);
  1180. }
  1181. /* }}} */
  1182. /* {{{ mysqlnd_res::fetch_row */
  1183. static enum_func_status
  1184. MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
  1185. {
  1186. const mysqlnd_fetch_row_func f = result->stored_data? result->stored_data->m.fetch_row:(result->unbuf? result->unbuf->m.fetch_row:NULL);
  1187. if (f) {
  1188. return f(result, param, flags, fetched_anything TSRMLS_CC);
  1189. }
  1190. *fetched_anything = FALSE;
  1191. return PASS;
  1192. }
  1193. /* }}} */
  1194. #define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY 2
  1195. /* {{{ mysqlnd_res::store_result_fetch_data */
  1196. enum_func_status
  1197. MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result,
  1198. MYSQLND_RES_METADATA * meta,
  1199. MYSQLND_MEMORY_POOL_CHUNK ***row_buffers,
  1200. zend_bool binary_protocol TSRMLS_DC)
  1201. {
  1202. enum_func_status ret;
  1203. MYSQLND_PACKET_ROW * row_packet = NULL;
  1204. unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
  1205. MYSQLND_RES_BUFFERED *set;
  1206. DBG_ENTER("mysqlnd_res::store_result_fetch_data");
  1207. set = result->stored_data;
  1208. if (!set || !row_buffers) {
  1209. ret = FAIL;
  1210. goto end;
  1211. }
  1212. if (free_rows) {
  1213. *row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
  1214. if (!*row_buffers) {
  1215. SET_OOM_ERROR(*conn->error_info);
  1216. ret = FAIL;
  1217. goto end;
  1218. }
  1219. }
  1220. set->references = 1;
  1221. /* non-persistent */
  1222. row_packet = conn->protocol->m.get_row_packet(conn->protocol, FALSE TSRMLS_CC);
  1223. if (!row_packet) {
  1224. SET_OOM_ERROR(*conn->error_info);
  1225. ret = FAIL;
  1226. goto end;
  1227. }
  1228. row_packet->result_set_memory_pool = result->stored_data->result_set_memory_pool;
  1229. row_packet->field_count = meta->field_count;
  1230. row_packet->binary_protocol = binary_protocol;
  1231. row_packet->fields_metadata = meta->fields;
  1232. row_packet->bit_fields_count = meta->bit_fields_count;
  1233. row_packet->bit_fields_total_len = meta->bit_fields_total_len;
  1234. row_packet->skip_extraction = TRUE; /* let php_mysqlnd_rowp_read() not allocate row_packet->fields, we will do it */
  1235. while (FAIL != (ret = PACKET_READ(row_packet, conn)) && !row_packet->eof) {
  1236. if (!free_rows) {
  1237. uint64_t total_allocated_rows = free_rows = next_extend = next_extend * 11 / 10; /* extend with 10% */
  1238. MYSQLND_MEMORY_POOL_CHUNK ** new_row_buffers;
  1239. total_allocated_rows += set->row_count;
  1240. /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
  1241. if (total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
  1242. SET_OOM_ERROR(*conn->error_info);
  1243. ret = FAIL;
  1244. goto end;
  1245. }
  1246. new_row_buffers = mnd_perealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
  1247. if (!new_row_buffers) {
  1248. SET_OOM_ERROR(*conn->error_info);
  1249. ret = FAIL;
  1250. goto end;
  1251. }
  1252. *row_buffers = new_row_buffers;
  1253. }
  1254. free_rows--;
  1255. (*row_buffers)[set->row_count] = row_packet->row_buffer;
  1256. set->row_count++;
  1257. /* So row_packet's destructor function won't efree() it */
  1258. row_packet->fields = NULL;
  1259. row_packet->row_buffer = NULL;
  1260. /*
  1261. No need to FREE_ALLOCA as we can reuse the
  1262. 'lengths' and 'fields' arrays. For lengths its absolutely safe.
  1263. 'fields' is reused because the ownership of the strings has been
  1264. transferred above.
  1265. */
  1266. }
  1267. /* Overflow ? */
  1268. MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
  1269. binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
  1270. STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
  1271. set->row_count);
  1272. /* Finally clean */
  1273. if (row_packet->eof) {
  1274. memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
  1275. conn->upsert_status->warning_count = row_packet->warning_count;
  1276. conn->upsert_status->server_status = row_packet->server_status;
  1277. }
  1278. /* save some memory */
  1279. if (free_rows) {
  1280. /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
  1281. if (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *) > SIZE_MAX) {
  1282. SET_OOM_ERROR(*conn->error_info);
  1283. ret = FAIL;
  1284. goto end;
  1285. }
  1286. *row_buffers = mnd_perealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
  1287. }
  1288. if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
  1289. CONN_SET_STATE(conn, CONN_NEXT_RESULT_PENDING);
  1290. } else {
  1291. CONN_SET_STATE(conn, CONN_READY);
  1292. }
  1293. if (ret == FAIL) {
  1294. COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
  1295. } else {
  1296. /* libmysql's documentation says it should be so for SELECT statements */
  1297. conn->upsert_status->affected_rows = set->row_count;
  1298. }
  1299. DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u",
  1300. ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
  1301. end:
  1302. PACKET_FREE(row_packet);
  1303. DBG_INF_FMT("rows=%llu", (unsigned long long)result->stored_data->row_count);
  1304. DBG_RETURN(ret);
  1305. }
  1306. /* }}} */
  1307. /* {{{ mysqlnd_res::store_result */
  1308. static MYSQLND_RES *
  1309. MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
  1310. MYSQLND_CONN_DATA * const conn,
  1311. const unsigned int flags TSRMLS_DC)
  1312. {
  1313. enum_func_status ret;
  1314. MYSQLND_MEMORY_POOL_CHUNK ***row_buffers = NULL;
  1315. DBG_ENTER("mysqlnd_res::store_result");
  1316. /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
  1317. /* In case of error the reference will be released in free_result_internal() called indirectly by our caller */
  1318. result->conn = conn->m->get_reference(conn TSRMLS_CC);
  1319. result->type = MYSQLND_RES_NORMAL;
  1320. CONN_SET_STATE(conn, CONN_FETCHING_DATA);
  1321. if (flags & MYSQLND_STORE_NO_COPY) {
  1322. result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC);
  1323. if (!result->stored_data) {
  1324. SET_OOM_ERROR(*conn->error_info);
  1325. DBG_RETURN(NULL);
  1326. }
  1327. row_buffers = &result->stored_data->row_buffers;
  1328. } else if (flags & MYSQLND_STORE_COPY) {
  1329. result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_c_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC);
  1330. if (!result->stored_data) {
  1331. SET_OOM_ERROR(*conn->error_info);
  1332. DBG_RETURN(NULL);
  1333. }
  1334. row_buffers = &result->stored_data->row_buffers;
  1335. }
  1336. ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, flags & MYSQLND_STORE_PS TSRMLS_CC);
  1337. if (FAIL == ret) {
  1338. if (result->stored_data) {
  1339. COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
  1340. } else {
  1341. SET_OOM_ERROR(*conn->error_info);
  1342. }
  1343. DBG_RETURN(NULL);
  1344. } else {
  1345. /* Overflow ? */
  1346. if (flags & MYSQLND_STORE_NO_COPY) {
  1347. MYSQLND_RES_METADATA * meta = result->meta;
  1348. MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
  1349. if (set->row_count) {
  1350. /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */
  1351. if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) {
  1352. SET_OOM_ERROR(*conn->error_info);
  1353. DBG_RETURN(NULL);
  1354. }
  1355. /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */
  1356. set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *)));
  1357. if (!set->data) {
  1358. SET_OOM_ERROR(*conn->error_info);
  1359. DBG_RETURN(NULL);
  1360. }
  1361. memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *)));
  1362. }
  1363. /* Position at the first row */
  1364. set->data_cursor = set->data;
  1365. } else if (flags & MYSQLND_STORE_COPY) {
  1366. MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
  1367. set->current_row = 0;
  1368. set->initialized = mnd_pecalloc((set->row_count / 8) + 1, sizeof(zend_uchar), set->persistent); /* +1 for safety */
  1369. }
  1370. }
  1371. /* libmysql's documentation says it should be so for SELECT statements */
  1372. conn->upsert_status->affected_rows = result->stored_data->row_count;
  1373. DBG_RETURN(result);
  1374. }
  1375. /* }}} */
  1376. /* {{{ mysqlnd_res::skip_result */
  1377. static enum_func_status
  1378. MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC)
  1379. {
  1380. zend_bool fetched_anything;
  1381. DBG_ENTER("mysqlnd_res::skip_result");
  1382. /*
  1383. Unbuffered sets
  1384. A PS could be prepared - there is metadata and thus a stmt->result but the
  1385. fetch_row function isn't actually set (NULL), thus we have to skip these.
  1386. */
  1387. if (result->unbuf && !result->unbuf->eof_reached) {
  1388. DBG_INF("skipping result");
  1389. /* We have to fetch all data to clean the line */
  1390. MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
  1391. result->type == MYSQLND_RES_NORMAL? STAT_FLUSHED_NORMAL_SETS:
  1392. STAT_FLUSHED_PS_SETS);
  1393. while ((PASS == result->m.fetch_row(result, NULL, 0, &fetched_anything TSRMLS_CC)) && fetched_anything == TRUE) {
  1394. /* do nothing */;
  1395. }
  1396. }
  1397. DBG_RETURN(PASS);
  1398. }
  1399. /* }}} */
  1400. /* {{{ mysqlnd_res::free_result */
  1401. static enum_func_status
  1402. MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC)
  1403. {
  1404. DBG_ENTER("mysqlnd_res::free_result");
  1405. MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
  1406. implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
  1407. STAT_FREE_RESULT_EXPLICIT);
  1408. result->m.free_result_internal(result TSRMLS_CC);
  1409. DBG_RETURN(PASS);
  1410. }
  1411. /* }}} */
  1412. /* {{{ mysqlnd_res::data_seek */
  1413. static enum_func_status
  1414. MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_t row TSRMLS_DC)
  1415. {
  1416. DBG_ENTER("mysqlnd_res::data_seek");
  1417. DBG_INF_FMT("row=%lu", row);
  1418. DBG_RETURN(result->stored_data? result->stored_data->m.data_seek(result->stored_data, row TSRMLS_CC) : FAIL);
  1419. }
  1420. /* }}} */
  1421. /* {{{ mysqlnd_result_buffered_zval::data_seek */
  1422. static enum_func_status
  1423. MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC)
  1424. {
  1425. MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
  1426. DBG_ENTER("mysqlnd_result_buffered_zval::data_seek");
  1427. /* libmysql just moves to the end, it does traversing of a linked list */
  1428. if (row >= set->row_count) {
  1429. set->data_cursor = NULL;
  1430. } else {
  1431. set->data_cursor = set->data + row * result->field_count;
  1432. }
  1433. DBG_RETURN(PASS);
  1434. }
  1435. /* }}} */
  1436. /* {{{ mysqlnd_result_buffered_c::data_seek */
  1437. static enum_func_status
  1438. MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC)
  1439. {
  1440. MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
  1441. DBG_ENTER("mysqlnd_result_buffered_c::data_seek");
  1442. /* libmysql just moves to the end, it does traversing of a linked list */
  1443. if (row >= set->row_count) {
  1444. set->current_row = set->row_count;
  1445. } else {
  1446. set->current_row = row;
  1447. }
  1448. DBG_RETURN(PASS);
  1449. }
  1450. /* }}} */
  1451. /* {{{ mysqlnd_result_unbuffered::num_rows */
  1452. static uint64_t
  1453. MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC)
  1454. {
  1455. /* Be compatible with libmysql. We count row_count, but will return 0 */
  1456. return result->eof_reached? result->row_count:0;
  1457. }
  1458. /* }}} */
  1459. /* {{{ mysqlnd_result_buffered::num_rows */
  1460. static uint64_t
  1461. MYSQLND_METHOD(mysqlnd_result_buffered, num_rows)(const MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
  1462. {
  1463. return result->row_count;
  1464. }
  1465. /* }}} */
  1466. /* {{{ mysqlnd_res::num_rows */
  1467. static uint64_t
  1468. MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result TSRMLS_DC)
  1469. {
  1470. return result->stored_data?
  1471. result->stored_data->m.num_rows(result->stored_data TSRMLS_CC) :
  1472. (result->unbuf? result->unbuf->m.num_rows(result->unbuf TSRMLS_CC) : 0);
  1473. }
  1474. /* }}} */
  1475. /* {{{ mysqlnd_res::num_fields */
  1476. static unsigned int
  1477. MYSQLND_METHOD(mysqlnd_res, num_fields)(const MYSQLND_RES * const result TSRMLS_DC)
  1478. {
  1479. return result->field_count;
  1480. }
  1481. /* }}} */
  1482. /* {{{ mysqlnd_res::fetch_field */
  1483. static const MYSQLND_FIELD *
  1484. MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
  1485. {
  1486. DBG_ENTER("mysqlnd_res::fetch_field");
  1487. do {
  1488. if (result->meta) {
  1489. /*
  1490. We optimize the result set, so we don't convert all the data from raw buffer format to
  1491. zval arrays during store. In the case someone doesn't read all the lines this will
  1492. save time. However, when a metadata call is done, we need to calculate max_length.
  1493. We don't have control whether max_length will be used, unfortunately. Otherwise we
  1494. could have been able to skip that step.
  1495. Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
  1496. then we can have max_length as dynamic property, which will be calculated during runtime and
  1497. not during mysqli_fetch_field() time.
  1498. */
  1499. if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
  1500. DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
  1501. /* we have to initialize the rest to get the updated max length */
  1502. if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
  1503. result->conn->options->int_and_float_native TSRMLS_CC))
  1504. {
  1505. break;
  1506. }
  1507. }
  1508. DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC));
  1509. }
  1510. } while (0);
  1511. DBG_RETURN(NULL);
  1512. }
  1513. /* }}} */
  1514. /* {{{ mysqlnd_res::fetch_field_direct */
  1515. static const MYSQLND_FIELD *
  1516. MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
  1517. {
  1518. DBG_ENTER("mysqlnd_res::fetch_field_direct");
  1519. do {
  1520. if (result->meta) {
  1521. /*
  1522. We optimize the result set, so we don't convert all the data from raw buffer format to
  1523. zval arrays during store. In the case someone doesn't read all the lines this will
  1524. save time. However, when a metadata call is done, we need to calculate max_length.
  1525. We don't have control whether max_length will be used, unfortunately. Otherwise we
  1526. could have been able to skip that step.
  1527. Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata,
  1528. then we can have max_length as dynamic property, which will be calculated during runtime and
  1529. not during mysqli_fetch_field_direct() time.
  1530. */
  1531. if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
  1532. DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
  1533. /* we have to initialized the rest to get the updated max length */
  1534. if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
  1535. result->conn->options->int_and_float_native TSRMLS_CC))
  1536. {
  1537. break;
  1538. }
  1539. }
  1540. DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC));
  1541. }
  1542. } while (0);
  1543. DBG_RETURN(NULL);
  1544. }
  1545. /* }}} */
  1546. /* {{{ mysqlnd_res::fetch_field */
  1547. static const MYSQLND_FIELD *
  1548. MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC)
  1549. {
  1550. DBG_ENTER("mysqlnd_res::fetch_fields");
  1551. do {
  1552. if (result->meta) {
  1553. if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
  1554. /* we have to initialize the rest to get the updated max length */
  1555. if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
  1556. result->conn->options->int_and_float_native TSRMLS_CC))
  1557. {
  1558. break;
  1559. }
  1560. }
  1561. DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC));
  1562. }
  1563. } while (0);
  1564. DBG_RETURN(NULL);
  1565. }
  1566. /* }}} */
  1567. /* {{{ mysqlnd_res::field_seek */
  1568. static MYSQLND_FIELD_OFFSET
  1569. MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
  1570. {
  1571. return result->meta? result->meta->m->field_seek(result->meta, field_offset TSRMLS_CC) : 0;
  1572. }
  1573. /* }}} */
  1574. /* {{{ mysqlnd_res::field_tell */
  1575. static MYSQLND_FIELD_OFFSET
  1576. MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_DC)
  1577. {
  1578. return result->meta? result->meta->m->field_tell(result->meta TSRMLS_CC) : 0;
  1579. }
  1580. /* }}} */
  1581. /* {{{ mysqlnd_res::fetch_into */
  1582. static void
  1583. MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int flags,
  1584. zval *return_value,
  1585. enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC)
  1586. {
  1587. zend_bool fetched_anything;
  1588. DBG_ENTER("mysqlnd_res::fetch_into");
  1589. /*
  1590. Hint Zend how many elements we will have in the hash. Thus it won't
  1591. extend and rehash the hash constantly.
  1592. */
  1593. mysqlnd_array_init(return_value, mysqlnd_num_fields(result) * 2);
  1594. if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything TSRMLS_CC)) {
  1595. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading a row");
  1596. zval_dtor(return_value);
  1597. RETVAL_FALSE;
  1598. } else if (fetched_anything == FALSE) {
  1599. zval_dtor(return_value);
  1600. switch (extension) {
  1601. case MYSQLND_MYSQLI:
  1602. RETVAL_NULL();
  1603. break;
  1604. case MYSQLND_MYSQL:
  1605. RETVAL_FALSE;
  1606. break;
  1607. default:exit(0);
  1608. }
  1609. }
  1610. /*
  1611. return_value is IS_NULL for no more data and an array for data. Thus it's ok
  1612. to return here.
  1613. */
  1614. DBG_VOID_RETURN;
  1615. }
  1616. /* }}} */
  1617. /* {{{ mysqlnd_res::fetch_row_c */
  1618. static MYSQLND_ROW_C
  1619. MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC)
  1620. {
  1621. zend_bool fetched_anything;
  1622. MYSQLND_ROW_C ret = NULL;
  1623. DBG_ENTER("mysqlnd_res::fetch_row_c");
  1624. if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)) {
  1625. MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC);
  1626. } else if (result->unbuf && result->unbuf->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)) {
  1627. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC);
  1628. } else {
  1629. ret = NULL;
  1630. php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
  1631. }
  1632. DBG_RETURN(ret);
  1633. }
  1634. /* }}} */
  1635. /* {{{ mysqlnd_res::fetch_all */
  1636. static void
  1637. MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
  1638. {
  1639. zval *row;
  1640. ulong i = 0;
  1641. MYSQLND_RES_BUFFERED *set = result->stored_data;
  1642. DBG_ENTER("mysqlnd_res::fetch_all");
  1643. if ((!result->unbuf && !set)) {
  1644. php_error_docref(NULL TSRMLS_CC, E_WARNING, "fetch_all can be used only with buffered sets");
  1645. if (result->conn) {
  1646. SET_CLIENT_ERROR(*result->conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "fetch_all can be used only with buffered sets");
  1647. }
  1648. RETVAL_NULL();
  1649. DBG_VOID_RETURN;
  1650. }
  1651. /* 4 is a magic value. The cast is safe, if larger then the array will be later extended - no big deal :) */
  1652. mysqlnd_array_init(return_value, set? (unsigned int) set->row_count : 4);
  1653. do {
  1654. MAKE_STD_ZVAL(row);
  1655. mysqlnd_fetch_into(result, flags, row, MYSQLND_MYSQLI);
  1656. if (Z_TYPE_P(row) != IS_ARRAY) {
  1657. zval_ptr_dtor(&row);
  1658. break;
  1659. }
  1660. add_index_zval(return_value, i++, row);
  1661. } while (1);
  1662. DBG_VOID_RETURN;
  1663. }
  1664. /* }}} */
  1665. /* {{{ mysqlnd_res::fetch_field_data */
  1666. static void
  1667. MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int offset, zval *return_value TSRMLS_DC)
  1668. {
  1669. zval row;
  1670. zval **entry;
  1671. unsigned int i = 0;
  1672. DBG_ENTER("mysqlnd_res::fetch_field_data");
  1673. DBG_INF_FMT("offset=%u", offset);
  1674. /*
  1675. Hint Zend how many elements we will have in the hash. Thus it won't
  1676. extend and rehash the hash constantly.
  1677. */
  1678. INIT_PZVAL(&row);
  1679. mysqlnd_fetch_into(result, MYSQLND_FETCH_NUM, &row, MYSQLND_MYSQL);
  1680. if (Z_TYPE(row) != IS_ARRAY) {
  1681. zval_dtor(&row);
  1682. RETVAL_NULL();
  1683. DBG_VOID_RETURN;
  1684. }
  1685. zend_hash_internal_pointer_reset(Z_ARRVAL(row));
  1686. while (i++ < offset) {
  1687. zend_hash_move_forward(Z_ARRVAL(row));
  1688. zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
  1689. }
  1690. zend_hash_get_current_data(Z_ARRVAL(row), (void **)&entry);
  1691. *return_value = **entry;
  1692. zval_copy_ctor(return_value);
  1693. Z_SET_REFCOUNT_P(return_value, 1);
  1694. zval_dtor(&row);
  1695. DBG_VOID_RETURN;
  1696. }
  1697. /* }}} */
  1698. MYSQLND_CLASS_METHODS_START(mysqlnd_res)
  1699. MYSQLND_METHOD(mysqlnd_res, fetch_row),
  1700. MYSQLND_METHOD(mysqlnd_res, use_result),
  1701. MYSQLND_METHOD(mysqlnd_res, store_result),
  1702. MYSQLND_METHOD(mysqlnd_res, fetch_into),
  1703. MYSQLND_METHOD(mysqlnd_res, fetch_row_c),
  1704. MYSQLND_METHOD(mysqlnd_res, fetch_all),
  1705. MYSQLND_METHOD(mysqlnd_res, fetch_field_data),
  1706. MYSQLND_METHOD(mysqlnd_res, num_rows),
  1707. MYSQLND_METHOD(mysqlnd_res, num_fields),
  1708. MYSQLND_METHOD(mysqlnd_res, skip_result),
  1709. MYSQLND_METHOD(mysqlnd_res, data_seek),
  1710. MYSQLND_METHOD(mysqlnd_res, field_seek),
  1711. MYSQLND_METHOD(mysqlnd_res, field_tell),
  1712. MYSQLND_METHOD(mysqlnd_res, fetch_field),
  1713. MYSQLND_METHOD(mysqlnd_res, fetch_field_direct),
  1714. MYSQLND_METHOD(mysqlnd_res, fetch_fields),
  1715. MYSQLND_METHOD(mysqlnd_res, read_result_metadata),
  1716. MYSQLND_METHOD(mysqlnd_res, fetch_lengths),
  1717. MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data),
  1718. MYSQLND_METHOD(mysqlnd_res, free_result_buffers),
  1719. MYSQLND_METHOD(mysqlnd_res, free_result),
  1720. MYSQLND_METHOD(mysqlnd_res, free_result_internal),
  1721. MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal),
  1722. mysqlnd_result_meta_init
  1723. MYSQLND_CLASS_METHODS_END;
  1724. MYSQLND_CLASS_METHODS_START(mysqlnd_result_unbuffered)
  1725. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row),
  1726. NULL, /* row_decoder */
  1727. MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows),
  1728. MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths),
  1729. MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data),
  1730. MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)
  1731. MYSQLND_CLASS_METHODS_END;
  1732. MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered)
  1733. NULL, /* fetch_row */
  1734. NULL, /* row_decoder */
  1735. MYSQLND_METHOD(mysqlnd_result_buffered, num_rows),
  1736. NULL, /* fetch_lengths */
  1737. NULL, /* data_seek */
  1738. NULL, /* initialize_result_set_rest */
  1739. MYSQLND_METHOD(mysqlnd_result_buffered, free_result)
  1740. MYSQLND_CLASS_METHODS_END;
  1741. /* {{{ mysqlnd_result_init */
  1742. PHPAPI MYSQLND_RES *
  1743. mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
  1744. {
  1745. size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
  1746. MYSQLND_RES * ret = mnd_pecalloc(1, alloc_size, persistent);
  1747. DBG_ENTER("mysqlnd_result_init");
  1748. if (!ret) {
  1749. DBG_RETURN(NULL);
  1750. }
  1751. ret->persistent = persistent;
  1752. ret->field_count = field_count;
  1753. ret->m = *mysqlnd_result_get_methods();
  1754. DBG_RETURN(ret);
  1755. }
  1756. /* }}} */
  1757. /* {{{ mysqlnd_result_unbuffered_init */
  1758. PHPAPI MYSQLND_RES_UNBUFFERED *
  1759. mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
  1760. {
  1761. size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
  1762. MYSQLND_RES_UNBUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent);
  1763. DBG_ENTER("mysqlnd_result_unbuffered_init");
  1764. if (!ret) {
  1765. DBG_RETURN(NULL);
  1766. }
  1767. if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
  1768. mnd_pefree(ret, persistent);
  1769. DBG_RETURN(NULL);
  1770. }
  1771. if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
  1772. mnd_efree(ret->lengths);
  1773. mnd_pefree(ret, persistent);
  1774. DBG_RETURN(NULL);
  1775. }
  1776. ret->persistent = persistent;
  1777. ret->field_count= field_count;
  1778. ret->ps = ps;
  1779. ret->m = *mysqlnd_result_unbuffered_get_methods();
  1780. if (ps) {
  1781. ret->m.fetch_lengths = NULL; /* makes no sense */
  1782. ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
  1783. } else {
  1784. ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval;
  1785. }
  1786. DBG_RETURN(ret);
  1787. }
  1788. /* }}} */
  1789. /* {{{ mysqlnd_result_buffered_zval_init */
  1790. PHPAPI MYSQLND_RES_BUFFERED_ZVAL *
  1791. mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
  1792. {
  1793. size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
  1794. MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent);
  1795. DBG_ENTER("mysqlnd_result_buffered_zval_init");
  1796. if (!ret) {
  1797. DBG_RETURN(NULL);
  1798. }
  1799. if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
  1800. mnd_pefree(ret, persistent);
  1801. DBG_RETURN(NULL);
  1802. }
  1803. if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
  1804. mnd_efree(ret->lengths);
  1805. mnd_pefree(ret, persistent);
  1806. DBG_RETURN(NULL);
  1807. }
  1808. ret->persistent = persistent;
  1809. ret->field_count= field_count;
  1810. ret->ps = ps;
  1811. ret->m = *mysqlnd_result_buffered_get_methods();
  1812. ret->type = MYSQLND_BUFFERED_TYPE_ZVAL;
  1813. if (ps) {
  1814. ret->m.fetch_lengths = NULL; /* makes no sense */
  1815. ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
  1816. } else {
  1817. ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval;
  1818. }
  1819. ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row);
  1820. ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths);
  1821. ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek);
  1822. ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest);
  1823. DBG_RETURN(ret);
  1824. }
  1825. /* }}} */
  1826. /* {{{ mysqlnd_result_buffered_c_init */
  1827. PHPAPI MYSQLND_RES_BUFFERED_C *
  1828. mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
  1829. {
  1830. size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
  1831. MYSQLND_RES_BUFFERED_C * ret = mnd_pecalloc(1, alloc_size, persistent);
  1832. DBG_ENTER("mysqlnd_result_buffered_c_init");
  1833. if (!ret) {
  1834. DBG_RETURN(NULL);
  1835. }
  1836. if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
  1837. mnd_pefree(ret, persistent);
  1838. DBG_RETURN(NULL);
  1839. }
  1840. if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
  1841. mnd_efree(ret->lengths);
  1842. mnd_pefree(ret, persistent);
  1843. DBG_RETURN(NULL);
  1844. }
  1845. ret->persistent = persistent;
  1846. ret->field_count= field_count;
  1847. ret->ps = ps;
  1848. ret->m = *mysqlnd_result_buffered_get_methods();
  1849. ret->type = MYSQLND_BUFFERED_TYPE_C;
  1850. if (ps) {
  1851. ret->m.fetch_lengths = NULL; /* makes no sense */
  1852. ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
  1853. } else {
  1854. ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_c;
  1855. }
  1856. ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row);
  1857. ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths);
  1858. ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek);
  1859. ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest);
  1860. DBG_RETURN(ret);
  1861. }
  1862. /* }}} */
  1863. /*
  1864. * Local variables:
  1865. * tab-width: 4
  1866. * c-basic-offset: 4
  1867. * End:
  1868. * vim600: noet sw=4 ts=4 fdm=marker
  1869. * vim<600: noet sw=4 ts=4
  1870. */