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

Generated by: LCOV version 1.10

Generated at Sun, 06 Apr 2014 17:31:04 +0000 (9 days ago)

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