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: 645 766 84.2 %
Date: 2014-04-18 Functions: 34 36 94.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Fri, 18 Apr 2014 07:01:30 +0000 (6 days ago)

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