PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - mysqlnd - mysqlnd_result.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 658
Code covered: 91.0 % Executed lines: 599
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:33 +0000 (33 hours ago)

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