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: 780 932 83.7 %
Date: 2016-07-26 Functions: 46 47 97.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Tue, 26 Jul 2016 17:07:38 +0000 (3 days ago)

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