root/ext/mysqlnd/mysqlnd_result.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MYSQLND_METHOD
  2. MYSQLND_METHOD
  3. MYSQLND_METHOD
  4. MYSQLND_METHOD
  5. MYSQLND_METHOD
  6. MYSQLND_METHOD
  7. mysqlnd_query_read_result_set_header
  8. MYSQLND_METHOD
  9. MYSQLND_METHOD
  10. MYSQLND_METHOD
  11. MYSQLND_METHOD
  12. MYSQLND_METHOD
  13. MYSQLND_METHOD
  14. MYSQLND_METHOD
  15. MYSQLND_METHOD
  16. MYSQLND_METHOD
  17. MYSQLND_METHOD
  18. MYSQLND_METHOD
  19. MYSQLND_METHOD
  20. MYSQLND_METHOD
  21. MYSQLND_CLASS_METHODS_START
  22. mysqlnd_result_unbuffered_init
  23. mysqlnd_result_buffered_zval_init
  24. mysqlnd_result_buffered_c_init

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

/* [<][>][^][v][top][bottom][index][help] */