PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/mysqlnd - mysqlnd_result.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 775 911 85.1 %
Date: 2014-10-30 Functions: 47 47 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Thu, 30 Oct 2014 07:41:34 +0000 (29 hours ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.