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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:04 +0000 (3 days ago)

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