root/ext/mysqlnd/mysqlnd_result.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mysqlnd_rset_zval_ptr_dtor
  2. MYSQLND_METHOD
  3. MYSQLND_METHOD
  4. MYSQLND_METHOD
  5. MYSQLND_METHOD
  6. MYSQLND_METHOD
  7. MYSQLND_METHOD
  8. mysqlnd_query_read_result_set_header
  9. MYSQLND_METHOD
  10. MYSQLND_METHOD
  11. MYSQLND_METHOD
  12. MYSQLND_METHOD
  13. MYSQLND_METHOD
  14. MYSQLND_METHOD
  15. MYSQLND_METHOD
  16. MYSQLND_METHOD
  17. MYSQLND_METHOD
  18. MYSQLND_METHOD
  19. MYSQLND_METHOD
  20. MYSQLND_METHOD
  21. MYSQLND_METHOD
  22. MYSQLND_CLASS_METHODS_START
  23. mysqlnd_result_unbuffered_init
  24. mysqlnd_result_buffered_zval_init
  25. mysqlnd_result_buffered_c_init

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

/* [<][>][^][v][top][bottom][index][help] */