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