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: 626 738 84.8 %
Date: 2014-04-10 Functions: 34 34 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Thu, 10 Apr 2014 08:53:52 +0000 (8 days ago)

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