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

Generated by: LCOV version 1.10

Generated at Wed, 27 Apr 2016 15:51:42 +0000 (3 days ago)

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