Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_ps.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_ps.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_priv.h"
! 26: #include "mysqlnd_result.h"
! 27: #include "mysqlnd_result_meta.h"
! 28: #include "mysqlnd_statistics.h"
! 29: #include "mysqlnd_debug.h"
! 30: #include "mysqlnd_block_alloc.h"
! 31:
! 32:
! 33: #define MYSQLND_SILENT
! 34:
! 35:
! 36: const char * const mysqlnd_not_bound_as_blob = "Can't send long data for non-string/non-binary data types";
! 37: const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
! 38:
! 39: static struct st_mysqlnd_stmt_methods *mysqlnd_stmt_methods;
! 40:
! 41: /* Exported by mysqlnd_ps_codec.c */
! 42: enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
! 43:
! 44: enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param,
! 45: unsigned int flags,
! 46: zend_bool *fetched_anything TSRMLS_DC);
! 47:
! 48: enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
! 49: unsigned int flags,
! 50: zend_bool *fetched_anything TSRMLS_DC);
! 51:
! 52: static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
! 53: static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
! 54:
! 55: /* {{{ mysqlnd_stmt::store_result */
! 56: static MYSQLND_RES *
! 57: MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
! 58: {
! 59: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 60: enum_func_status ret;
! 61: MYSQLND * conn;
! 62: MYSQLND_RES * result;
! 63: zend_bool to_cache = FALSE;
! 64:
! 65: DBG_ENTER("mysqlnd_stmt::store_result");
! 66: if (!stmt || !stmt->conn || !stmt->result) {
! 67: DBG_RETURN(NULL);
! 68: }
! 69: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 70:
! 71: conn = stmt->conn;
! 72:
! 73: /* be compliant with libmysql - NULL will turn */
! 74: if (!stmt->field_count) {
! 75: DBG_RETURN(NULL);
! 76: }
! 77:
! 78: if (stmt->cursor_exists) {
! 79: /* Silently convert buffered to unbuffered, for now */
! 80: DBG_RETURN(s->m->use_result(s TSRMLS_CC));
! 81: }
! 82:
! 83: /* Nothing to store for UPSERT/LOAD DATA*/
! 84: if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA ||
! 85: stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE)
! 86: {
! 87: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
! 88: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 89: DBG_RETURN(NULL);
! 90: }
! 91:
! 92: stmt->default_rset_handler = s->m->store_result;
! 93:
! 94: SET_EMPTY_ERROR(stmt->error_info);
! 95: SET_EMPTY_ERROR(stmt->conn->error_info);
! 96: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_BUFFERED_SETS);
! 97:
! 98: result = stmt->result;
! 99: result->type = MYSQLND_RES_PS_BUF;
! 100: result->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
! 101: result->m.fetch_lengths = NULL;/* makes no sense */
! 102:
! 103: result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
! 104:
! 105: ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE, to_cache TSRMLS_CC);
! 106:
! 107: if (PASS == ret) {
! 108: /* libmysql API docs say it should be so for SELECT statements */
! 109: stmt->upsert_status.affected_rows = stmt->result->stored_data->row_count;
! 110:
! 111: stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
! 112: } else {
! 113: conn->error_info = result->stored_data->error_info;
! 114: stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
! 115: mnd_efree(stmt->result);
! 116: stmt->result = NULL;
! 117: stmt->state = MYSQLND_STMT_PREPARED;
! 118: }
! 119:
! 120: DBG_RETURN(result);
! 121: }
! 122: /* }}} */
! 123:
! 124:
! 125: /* {{{ mysqlnd_stmt::get_result */
! 126: static MYSQLND_RES *
! 127: MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
! 128: {
! 129: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 130: MYSQLND * conn;
! 131: MYSQLND_RES *result;
! 132:
! 133: DBG_ENTER("mysqlnd_stmt::get_result");
! 134: if (!stmt || !stmt->conn || !stmt->result) {
! 135: DBG_RETURN(NULL);
! 136: }
! 137: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 138:
! 139: conn = stmt->conn;
! 140:
! 141: /* be compliant with libmysql - NULL will turn */
! 142: if (!stmt->field_count) {
! 143: DBG_RETURN(NULL);
! 144: }
! 145:
! 146: if (stmt->cursor_exists) {
! 147: /* Silently convert buffered to unbuffered, for now */
! 148: DBG_RETURN(s->m->use_result(s TSRMLS_CC));
! 149: }
! 150:
! 151: /* Nothing to store for UPSERT/LOAD DATA*/
! 152: if (CONN_GET_STATE(conn) != CONN_FETCHING_DATA || stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 153: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
! 154: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 155: DBG_RETURN(NULL);
! 156: }
! 157:
! 158: SET_EMPTY_ERROR(stmt->error_info);
! 159: SET_EMPTY_ERROR(stmt->conn->error_info);
! 160: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
! 161:
! 162: do {
! 163: result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC);
! 164: if (!result) {
! 165: SET_OOM_ERROR(stmt->conn->error_info);
! 166: break;
! 167: }
! 168:
! 169: result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
! 170: if (!result->meta) {
! 171: SET_OOM_ERROR(stmt->conn->error_info);
! 172: break;
! 173: }
! 174:
! 175: if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
! 176: stmt->upsert_status.affected_rows = result->stored_data->row_count;
! 177: stmt->state = MYSQLND_STMT_PREPARED;
! 178: result->type = MYSQLND_RES_PS_BUF;
! 179: } else {
! 180: stmt->error_info = conn->error_info;
! 181: stmt->state = MYSQLND_STMT_PREPARED;
! 182: break;
! 183: }
! 184: DBG_RETURN(result);
! 185: } while (0);
! 186:
! 187: if (result) {
! 188: result->m.free_result(result, TRUE TSRMLS_CC);
! 189: }
! 190: DBG_RETURN(NULL);
! 191: }
! 192: /* }}} */
! 193:
! 194:
! 195: /* {{{ mysqlnd_stmt::more_results */
! 196: static zend_bool
! 197: MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC)
! 198: {
! 199: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 200: DBG_ENTER("mysqlnd_stmt::more_results");
! 201: /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
! 202: DBG_RETURN((stmt && stmt->conn && (stmt->conn->upsert_status.server_status &
! 203: SERVER_MORE_RESULTS_EXISTS))?
! 204: TRUE:
! 205: FALSE);
! 206: }
! 207: /* }}} */
! 208:
! 209:
! 210: /* {{{ mysqlnd_stmt::next_result */
! 211: static enum_func_status
! 212: MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC)
! 213: {
! 214: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 215: MYSQLND * conn;
! 216:
! 217: DBG_ENTER("mysqlnd_stmt::next_result");
! 218: if (!stmt || !stmt->conn || !stmt->result) {
! 219: DBG_RETURN(FAIL);
! 220: }
! 221: conn = stmt->conn;
! 222: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 223:
! 224: if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS)) {
! 225: DBG_RETURN(FAIL);
! 226: }
! 227:
! 228: DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
! 229: DBG_INF_FMT("server_status=%u cursor=%u", conn->upsert_status.server_status, conn->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
! 230:
! 231: /* Free space for next result */
! 232: s->m->free_stmt_content(s TSRMLS_CC);
! 233: {
! 234: enum_func_status ret = s->m->parse_execute_response(s TSRMLS_CC);
! 235: DBG_RETURN(ret);
! 236: }
! 237: }
! 238: /* }}} */
! 239:
! 240:
! 241: /* {{{ mysqlnd_stmt_skip_metadata */
! 242: static enum_func_status
! 243: mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC)
! 244: {
! 245: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 246: /* Follows parameter metadata, we have just to skip it, as libmysql does */
! 247: unsigned int i = 0;
! 248: enum_func_status ret = FAIL;
! 249: MYSQLND_PACKET_RES_FIELD * field_packet;
! 250:
! 251: DBG_ENTER("mysqlnd_stmt_skip_metadata");
! 252: if (!stmt || !stmt->conn || !stmt->conn->protocol) {
! 253: DBG_RETURN(FAIL);
! 254: }
! 255: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 256:
! 257: field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
! 258: if (!field_packet) {
! 259: SET_OOM_ERROR(stmt->error_info);
! 260: SET_OOM_ERROR(stmt->conn->error_info);
! 261: } else {
! 262: ret = PASS;
! 263: field_packet->skip_parsing = TRUE;
! 264: for (;i < stmt->param_count; i++) {
! 265: if (FAIL == PACKET_READ(field_packet, stmt->conn)) {
! 266: ret = FAIL;
! 267: break;
! 268: }
! 269: }
! 270: PACKET_FREE(field_packet);
! 271: }
! 272:
! 273: DBG_RETURN(ret);
! 274: }
! 275: /* }}} */
! 276:
! 277:
! 278: /* {{{ mysqlnd_stmt_read_prepare_response */
! 279: static enum_func_status
! 280: mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC)
! 281: {
! 282: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 283: MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp;
! 284: enum_func_status ret = FAIL;
! 285:
! 286: DBG_ENTER("mysqlnd_stmt_read_prepare_response");
! 287: if (!stmt || !stmt->conn || !stmt->conn->protocol) {
! 288: DBG_RETURN(FAIL);
! 289: }
! 290: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 291:
! 292: prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
! 293: if (!prepare_resp) {
! 294: SET_OOM_ERROR(stmt->error_info);
! 295: SET_OOM_ERROR(stmt->conn->error_info);
! 296: goto done;
! 297: }
! 298:
! 299: if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) {
! 300: goto done;
! 301: }
! 302:
! 303: if (0xFF == prepare_resp->error_code) {
! 304: stmt->error_info = stmt->conn->error_info = prepare_resp->error_info;
! 305: goto done;
! 306: }
! 307: ret = PASS;
! 308: stmt->stmt_id = prepare_resp->stmt_id;
! 309: stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp->warning_count;
! 310: stmt->field_count = stmt->conn->field_count = prepare_resp->field_count;
! 311: stmt->param_count = prepare_resp->param_count;
! 312: done:
! 313: PACKET_FREE(prepare_resp);
! 314:
! 315: DBG_RETURN(ret);
! 316: }
! 317: /* }}} */
! 318:
! 319:
! 320: /* {{{ mysqlnd_stmt_prepare_read_eof */
! 321: static enum_func_status
! 322: mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC)
! 323: {
! 324: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 325: MYSQLND_PACKET_EOF * fields_eof;
! 326: enum_func_status ret = FAIL;
! 327:
! 328: DBG_ENTER("mysqlnd_stmt_prepare_read_eof");
! 329: if (!stmt || !stmt->conn || !stmt->conn->protocol) {
! 330: DBG_RETURN(FAIL);
! 331: }
! 332: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 333:
! 334: fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC);
! 335: if (!fields_eof) {
! 336: SET_OOM_ERROR(stmt->error_info);
! 337: SET_OOM_ERROR(stmt->conn->error_info);
! 338: } else {
! 339: if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) {
! 340: if (stmt->result) {
! 341: stmt->result->m.free_result_contents(stmt->result TSRMLS_CC);
! 342: mnd_efree(stmt->result);
! 343: memset(stmt, 0, sizeof(MYSQLND_STMT_DATA));
! 344: stmt->state = MYSQLND_STMT_INITTED;
! 345: }
! 346: } else {
! 347: stmt->upsert_status.server_status = fields_eof->server_status;
! 348: stmt->upsert_status.warning_count = fields_eof->warning_count;
! 349: stmt->state = MYSQLND_STMT_PREPARED;
! 350: }
! 351: PACKET_FREE(fields_eof);
! 352: }
! 353:
! 354: DBG_RETURN(ret);
! 355: }
! 356: /* }}} */
! 357:
! 358:
! 359: /* {{{ mysqlnd_stmt::prepare */
! 360: static enum_func_status
! 361: MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC)
! 362: {
! 363: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 364: MYSQLND_STMT * s_to_prepare = s;
! 365: MYSQLND_STMT_DATA * stmt_to_prepare = stmt;
! 366:
! 367: DBG_ENTER("mysqlnd_stmt::prepare");
! 368: if (!stmt || !stmt->conn) {
! 369: DBG_RETURN(FAIL);
! 370: }
! 371: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 372:
! 373: SET_ERROR_AFF_ROWS(stmt);
! 374: SET_ERROR_AFF_ROWS(stmt->conn);
! 375:
! 376: SET_EMPTY_ERROR(stmt->error_info);
! 377: SET_EMPTY_ERROR(stmt->conn->error_info);
! 378:
! 379: if (stmt->state > MYSQLND_STMT_INITTED) {
! 380: /* See if we have to clean the wire */
! 381: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 382: /* Do implicit use_result and then flush the result */
! 383: stmt->default_rset_handler = s->m->use_result;
! 384: stmt->default_rset_handler(s TSRMLS_CC);
! 385: }
! 386: /* No 'else' here please :) */
! 387: if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) {
! 388: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
! 389: }
! 390: /*
! 391: Create a new test statement, which we will prepare, but if anything
! 392: fails, we will scrap it.
! 393: */
! 394: s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC);
! 395: if (!s_to_prepare) {
! 396: goto fail;
! 397: }
! 398: stmt_to_prepare = s_to_prepare->data;
! 399: }
! 400:
! 401: if (FAIL == stmt_to_prepare->conn->m->simple_command(stmt_to_prepare->conn, COM_STMT_PREPARE, query, query_len, PROT_LAST, FALSE, TRUE TSRMLS_CC) ||
! 402: FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC))
! 403: {
! 404: goto fail;
! 405: }
! 406:
! 407: if (stmt_to_prepare->param_count) {
! 408: if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) ||
! 409: FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
! 410: {
! 411: goto fail;
! 412: }
! 413: }
! 414:
! 415: /*
! 416: Read metadata only if there is actual result set.
! 417: Beware that SHOW statements bypass the PS framework and thus they send
! 418: no metadata at prepare.
! 419: */
! 420: if (stmt_to_prepare->field_count) {
! 421: MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC);
! 422: if (!result) {
! 423: SET_OOM_ERROR(stmt->conn->error_info);
! 424: goto fail;
! 425: }
! 426: /* Allocate the result now as it is needed for the reading of metadata */
! 427: stmt_to_prepare->result = result;
! 428:
! 429: result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
! 430:
! 431: result->type = MYSQLND_RES_PS_BUF;
! 432:
! 433: if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) ||
! 434: FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC))
! 435: {
! 436: goto fail;
! 437: }
! 438: }
! 439:
! 440: if (stmt_to_prepare != stmt) {
! 441: /* swap */
! 442: size_t real_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
! 443: char * tmp_swap = mnd_malloc(real_size);
! 444: memcpy(tmp_swap, s, real_size);
! 445: memcpy(s, s_to_prepare, real_size);
! 446: memcpy(s_to_prepare, tmp_swap, real_size);
! 447: mnd_free(tmp_swap);
! 448: {
! 449: MYSQLND_STMT_DATA * tmp_swap_data = stmt_to_prepare;
! 450: stmt_to_prepare = stmt;
! 451: stmt = tmp_swap_data;
! 452: }
! 453: s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
! 454: }
! 455: stmt->state = MYSQLND_STMT_PREPARED;
! 456: DBG_INF("PASS");
! 457: DBG_RETURN(PASS);
! 458:
! 459: fail:
! 460: if (stmt_to_prepare != stmt && s_to_prepare) {
! 461: s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC);
! 462: }
! 463: stmt->state = MYSQLND_STMT_INITTED;
! 464:
! 465: DBG_INF("FAIL");
! 466: DBG_RETURN(FAIL);
! 467: }
! 468: /* }}} */
! 469:
! 470:
! 471: /* {{{ mysqlnd_stmt_execute_parse_response */
! 472: static enum_func_status
! 473: mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
! 474: {
! 475: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 476: enum_func_status ret;
! 477: MYSQLND * conn;
! 478:
! 479: DBG_ENTER("mysqlnd_stmt_execute_parse_response");
! 480: if (!stmt || !stmt->conn) {
! 481: DBG_RETURN(FAIL);
! 482: }
! 483: conn = stmt->conn;
! 484: CONN_SET_STATE(conn, CONN_QUERY_SENT);
! 485:
! 486: ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC);
! 487: if (ret == FAIL) {
! 488: stmt->error_info = conn->error_info;
! 489: stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
! 490: if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
! 491: /* close the statement here, the connection has been closed */
! 492: }
! 493: stmt->state = MYSQLND_STMT_PREPARED;
! 494: stmt->send_types_to_server = 1;
! 495: } else {
! 496: /*
! 497: stmt->send_types_to_server has already been set to 0 in
! 498: mysqlnd_stmt_execute_generate_request / mysqlnd_stmt_execute_store_params
! 499: In case there is a situation in which binding was done for integer and the
! 500: value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
! 501: to resend the types. Next execution will also need to resend the type.
! 502: */
! 503: SET_EMPTY_ERROR(stmt->error_info);
! 504: SET_EMPTY_ERROR(stmt->conn->error_info);
! 505: stmt->upsert_status = conn->upsert_status;
! 506: stmt->state = MYSQLND_STMT_EXECUTED;
! 507: if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
! 508: DBG_INF("PASS");
! 509: DBG_RETURN(PASS);
! 510: }
! 511:
! 512: stmt->result->type = MYSQLND_RES_PS_BUF;
! 513: if (!stmt->result->conn) {
! 514: /*
! 515: For SHOW we don't create (bypasses PS in server)
! 516: a result set at prepare and thus a connection was missing
! 517: */
! 518: stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
! 519: }
! 520:
! 521: /* Update stmt->field_count as SHOW sets it to 0 at prepare */
! 522: stmt->field_count = stmt->result->field_count = conn->field_count;
! 523: stmt->result->lengths = NULL;
! 524: if (stmt->field_count) {
! 525: stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
! 526: /*
! 527: We need to set this because the user might not call
! 528: use_result() or store_result() and we should be able to scrap the
! 529: data on the line, if he just decides to close the statement.
! 530: */
! 531: DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status,
! 532: stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
! 533:
! 534: if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
! 535: DBG_INF("cursor exists");
! 536: stmt->cursor_exists = TRUE;
! 537: CONN_SET_STATE(conn, CONN_READY);
! 538: /* Only cursor read */
! 539: stmt->default_rset_handler = s->m->use_result;
! 540: DBG_INF("use_result");
! 541: } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
! 542: DBG_INF("asked for cursor but got none");
! 543: /*
! 544: We have asked for CURSOR but got no cursor, because the condition
! 545: above is not fulfilled. Then...
! 546:
! 547: This is a single-row result set, a result set with no rows, EXPLAIN,
! 548: SHOW VARIABLES, or some other command which either a) bypasses the
! 549: cursors framework in the server and writes rows directly to the
! 550: network or b) is more efficient if all (few) result set rows are
! 551: precached on client and server's resources are freed.
! 552: */
! 553: /* preferred is buffered read */
! 554: stmt->default_rset_handler = s->m->store_result;
! 555: DBG_INF("store_result");
! 556: } else {
! 557: DBG_INF("no cursor");
! 558: /* preferred is unbuffered read */
! 559: stmt->default_rset_handler = s->m->use_result;
! 560: DBG_INF("use_result");
! 561: }
! 562: }
! 563: }
! 564: #ifndef MYSQLND_DONT_SKIP_OUT_PARAMS_RESULTSET
! 565: if (stmt->upsert_status.server_status & SERVER_PS_OUT_PARAMS) {
! 566: s->m->free_stmt_content(s TSRMLS_CC);
! 567: DBG_INF("PS OUT Variable RSet, skipping");
! 568: /* OUT params result set. Skip for now to retain compatibility */
! 569: ret = mysqlnd_stmt_execute_parse_response(s TSRMLS_CC);
! 570: }
! 571: #endif
! 572:
! 573: DBG_INF(ret == PASS? "PASS":"FAIL");
! 574: DBG_RETURN(ret);
! 575: }
! 576: /* }}} */
! 577:
! 578:
! 579: /* {{{ mysqlnd_stmt::execute */
! 580: static enum_func_status
! 581: MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
! 582: {
! 583: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 584: enum_func_status ret;
! 585: MYSQLND * conn;
! 586: zend_uchar *request = NULL;
! 587: size_t request_len;
! 588: zend_bool free_request;
! 589:
! 590: DBG_ENTER("mysqlnd_stmt::execute");
! 591: if (!stmt || !stmt->conn) {
! 592: DBG_RETURN(FAIL);
! 593: }
! 594: conn = stmt->conn;
! 595: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 596:
! 597: SET_ERROR_AFF_ROWS(stmt);
! 598: SET_ERROR_AFF_ROWS(stmt->conn);
! 599:
! 600: if (stmt->result && stmt->state >= MYSQLND_STMT_PREPARED && stmt->field_count) {
! 601: /*
! 602: We don need to copy the data from the buffers which we will clean.
! 603: Because it has already been copied. See
! 604: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 605: */
! 606: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 607: if (stmt->result_bind &&
! 608: stmt->result_zvals_separated_once == TRUE &&
! 609: stmt->state >= MYSQLND_STMT_USER_FETCHING)
! 610: {
! 611: /*
! 612: We need to copy the data from the buffers which we will clean.
! 613: The bound variables point to them only if the user has started
! 614: to fetch data (MYSQLND_STMT_USER_FETCHING).
! 615: We need to check 'result_zvals_separated_once' or we will leak
! 616: in the following scenario
! 617: prepare("select 1 from dual");
! 618: execute();
! 619: fetch(); <-- no binding, but that's not a problem
! 620: bind_result();
! 621: execute(); <-- here we will leak because we separate without need
! 622: */
! 623: unsigned int i;
! 624: for (i = 0; i < stmt->field_count; i++) {
! 625: if (stmt->result_bind[i].bound == TRUE) {
! 626: zval_copy_ctor(stmt->result_bind[i].zv);
! 627: }
! 628: }
! 629: }
! 630: #endif
! 631:
! 632: /*
! 633: If right after execute() we have to call the appropriate
! 634: use_result() or store_result() and clean.
! 635: */
! 636: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 637: DBG_INF("fetching result set header");
! 638: /* Do implicit use_result and then flush the result */
! 639: stmt->default_rset_handler = s->m->use_result;
! 640: stmt->default_rset_handler(s TSRMLS_CC);
! 641: }
! 642:
! 643: if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 644: DBG_INF("skipping result");
! 645: /* Flush if anything is left and unbuffered set */
! 646: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
! 647: }
! 648:
! 649: if (stmt->state > MYSQLND_STMT_PREPARED) {
! 650: /* As the buffers have been freed, we should go back to PREPARED */
! 651: stmt->state = MYSQLND_STMT_PREPARED;
! 652: }
! 653:
! 654: /*
! 655: Executed, but the user hasn't started to fetch
! 656: This will clean also the metadata, but after the EXECUTE call we will
! 657: have it again.
! 658: */
! 659: stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
! 660: } else if (stmt->state < MYSQLND_STMT_PREPARED) {
! 661: /* Only initted - error */
! 662: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
! 663: mysqlnd_out_of_sync);
! 664: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 665: DBG_INF("FAIL");
! 666: DBG_RETURN(FAIL);
! 667: }
! 668:
! 669: if (stmt->param_count) {
! 670: unsigned int i, not_bound = 0;
! 671: if (!stmt->param_bind) {
! 672: SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE,
! 673: "No data supplied for parameters in prepared statement");
! 674: DBG_INF("FAIL");
! 675: DBG_RETURN(FAIL);
! 676: }
! 677: for (i = 0; i < stmt->param_count; i++) {
! 678: if (stmt->param_bind[i].zv == NULL) {
! 679: not_bound++;
! 680: }
! 681: }
! 682: if (not_bound) {
! 683: char * msg;
! 684: spprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement",
! 685: not_bound, not_bound>1 ?"s":"");
! 686: SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg);
! 687: if (msg) {
! 688: efree(msg); /* allocated by spprintf */
! 689: }
! 690: DBG_INF("FAIL");
! 691: DBG_RETURN(FAIL);
! 692: }
! 693: }
! 694: ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC);
! 695: if (ret == PASS) {
! 696: /* support for buffer types should be added here ! */
! 697: ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len,
! 698: PROT_LAST /* we will handle the response packet*/,
! 699: FALSE, FALSE TSRMLS_CC);
! 700: } else {
! 701: SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM.");
! 702: }
! 703:
! 704: if (free_request) {
! 705: mnd_efree(request);
! 706: }
! 707:
! 708: if (ret == FAIL) {
! 709: stmt->error_info = conn->error_info;
! 710: DBG_INF("FAIL");
! 711: DBG_RETURN(FAIL);
! 712: }
! 713: stmt->execute_count++;
! 714:
! 715: ret = s->m->parse_execute_response(s TSRMLS_CC);
! 716:
! 717: DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
! 718:
! 719: if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status.affected_rows) {
! 720: MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status.affected_rows);
! 721: }
! 722: DBG_RETURN(ret);
! 723: }
! 724: /* }}} */
! 725:
! 726:
! 727: /* {{{ mysqlnd_stmt_fetch_row_buffered */
! 728: enum_func_status
! 729: mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
! 730: {
! 731: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
! 732: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 733: MYSQLND_RES_BUFFERED *set = result->stored_data;
! 734: unsigned int field_count = result->meta->field_count;
! 735:
! 736: DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
! 737: *fetched_anything = FALSE;
! 738: DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
! 739:
! 740: /* If we haven't read everything */
! 741: if (set->data_cursor &&
! 742: (set->data_cursor - set->data) < (set->row_count * field_count))
! 743: {
! 744: /* The user could have skipped binding - don't crash*/
! 745: if (stmt->result_bind) {
! 746: unsigned int i;
! 747: MYSQLND_RES_METADATA * meta = result->meta;
! 748: zval **current_row = set->data_cursor;
! 749:
! 750: if (NULL == current_row[0]) {
! 751: uint64_t row_num = (set->data_cursor - set->data) / field_count;
! 752: enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
! 753: current_row,
! 754: meta->field_count,
! 755: meta->fields,
! 756: result->stored_data->persistent,
! 757: result->conn->options.numeric_and_datetime_as_unicode,
! 758: result->conn->options.int_and_float_native,
! 759: result->conn->stats TSRMLS_CC);
! 760: if (PASS != rc) {
! 761: DBG_RETURN(FAIL);
! 762: }
! 763: set->initialized_rows++;
! 764: if (stmt->update_max_length) {
! 765: for (i = 0; i < result->field_count; i++) {
! 766: /*
! 767: NULL fields are 0 length, 0 is not more than 0
! 768: String of zero size, definitely can't be the next max_length.
! 769: Thus for NULL and zero-length we are quite efficient.
! 770: */
! 771: if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
! 772: unsigned long len = Z_STRLEN_P(current_row[i]);
! 773: if (meta->fields[i].max_length < len) {
! 774: meta->fields[i].max_length = len;
! 775: }
! 776: }
! 777: }
! 778: }
! 779: }
! 780:
! 781: for (i = 0; i < result->field_count; i++) {
! 782: /* Clean what we copied last time */
! 783: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 784: if (stmt->result_bind[i].zv) {
! 785: zval_dtor(stmt->result_bind[i].zv);
! 786: }
! 787: #endif
! 788: /* copy the type */
! 789: if (stmt->result_bind[i].bound == TRUE) {
! 790: DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
! 791: if (Z_TYPE_P(current_row[i]) != IS_NULL) {
! 792: /*
! 793: Copy the value.
! 794: Pre-condition is that the zvals in the result_bind buffer
! 795: have been ZVAL_NULL()-ed or to another simple type
! 796: (int, double, bool but not string). Because of the reference
! 797: counting the user can't delete the strings the variables point to.
! 798: */
! 799:
! 800: Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
! 801: stmt->result_bind[i].zv->value = current_row[i]->value;
! 802: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 803: zval_copy_ctor(stmt->result_bind[i].zv);
! 804: #endif
! 805: } else {
! 806: ZVAL_NULL(stmt->result_bind[i].zv);
! 807: }
! 808: }
! 809: }
! 810: }
! 811: set->data_cursor += field_count;
! 812: *fetched_anything = TRUE;
! 813: /* buffered result sets don't have a connection */
! 814: MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
! 815: DBG_INF("row fetched");
! 816: } else {
! 817: set->data_cursor = NULL;
! 818: DBG_INF("no more data");
! 819: }
! 820: DBG_INF("PASS");
! 821: DBG_RETURN(PASS);
! 822: }
! 823: /* }}} */
! 824:
! 825:
! 826: /* {{{ mysqlnd_stmt_fetch_row_unbuffered */
! 827: static enum_func_status
! 828: mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
! 829: {
! 830: enum_func_status ret;
! 831: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
! 832: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 833: MYSQLND_PACKET_ROW * row_packet;
! 834:
! 835: DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
! 836:
! 837: *fetched_anything = FALSE;
! 838:
! 839: if (result->unbuf->eof_reached) {
! 840: /* No more rows obviously */
! 841: DBG_INF("eof reached");
! 842: DBG_RETURN(PASS);
! 843: }
! 844: if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
! 845: SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
! 846: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 847: DBG_ERR("command out of sync");
! 848: DBG_RETURN(FAIL);
! 849: }
! 850: if (!(row_packet = result->row_packet)) {
! 851: DBG_RETURN(FAIL);
! 852: }
! 853:
! 854: /* Let the row packet fill our buffer and skip additional malloc + memcpy */
! 855: row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE;
! 856:
! 857: /*
! 858: If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
! 859: result->m.unbuffered_free_last_data() before it. The function returns always true.
! 860: */
! 861: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
! 862: unsigned int i, field_count = result->field_count;
! 863:
! 864: if (!row_packet->skip_extraction) {
! 865: result->m.unbuffered_free_last_data(result TSRMLS_CC);
! 866:
! 867: DBG_INF("extracting data");
! 868: result->unbuf->last_row_data = row_packet->fields;
! 869: result->unbuf->last_row_buffer = row_packet->row_buffer;
! 870: row_packet->fields = NULL;
! 871: row_packet->row_buffer = NULL;
! 872:
! 873: if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
! 874: result->unbuf->last_row_data,
! 875: row_packet->field_count,
! 876: row_packet->fields_metadata,
! 877: FALSE,
! 878: result->conn->options.numeric_and_datetime_as_unicode,
! 879: result->conn->options.int_and_float_native,
! 880: result->conn->stats TSRMLS_CC))
! 881: {
! 882: DBG_RETURN(FAIL);
! 883: }
! 884:
! 885: for (i = 0; i < field_count; i++) {
! 886: if (stmt->result_bind[i].bound == TRUE) {
! 887: zval *data = result->unbuf->last_row_data[i];
! 888: /*
! 889: stmt->result_bind[i].zv has been already destructed
! 890: in result->m.unbuffered_free_last_data()
! 891: */
! 892: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 893: zval_dtor(stmt->result_bind[i].zv);
! 894: #endif
! 895: if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
! 896: if (
! 897: (Z_TYPE_P(data) == IS_STRING
! 898: #if MYSQLND_UNICODE
! 899: || Z_TYPE_P(data) == IS_UNICODE
! 900: #endif
! 901: )
! 902: && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
! 903: {
! 904: result->meta->fields[i].max_length = Z_STRLEN_P(data);
! 905: }
! 906: stmt->result_bind[i].zv->value = data->value;
! 907: /* copied data, thus also the ownership. Thus null data */
! 908: ZVAL_NULL(data);
! 909: }
! 910: }
! 911: }
! 912: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
! 913: } else {
! 914: DBG_INF("skipping extraction");
! 915: /*
! 916: Data has been allocated and usually result->m.unbuffered_free_last_data()
! 917: frees it but we can't call this function as it will cause problems with
! 918: the bound variables. Thus we need to do part of what it does or Zend will
! 919: report leaks.
! 920: */
! 921: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
! 922: row_packet->row_buffer = NULL;
! 923: }
! 924:
! 925: result->unbuf->row_count++;
! 926: *fetched_anything = TRUE;
! 927: } else if (ret == FAIL) {
! 928: if (row_packet->error_info.error_no) {
! 929: stmt->conn->error_info = row_packet->error_info;
! 930: stmt->error_info = row_packet->error_info;
! 931: }
! 932: CONN_SET_STATE(result->conn, CONN_READY);
! 933: result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
! 934: } else if (row_packet->eof) {
! 935: DBG_INF("EOF");
! 936: /* Mark the connection as usable again */
! 937: result->unbuf->eof_reached = TRUE;
! 938: result->conn->upsert_status.warning_count = row_packet->warning_count;
! 939: result->conn->upsert_status.server_status = row_packet->server_status;
! 940: /*
! 941: result->row_packet will be cleaned when
! 942: destroying the result object
! 943: */
! 944: if (result->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) {
! 945: CONN_SET_STATE(result->conn, CONN_NEXT_RESULT_PENDING);
! 946: } else {
! 947: CONN_SET_STATE(result->conn, CONN_READY);
! 948: }
! 949: }
! 950:
! 951: DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
! 952: DBG_RETURN(ret);
! 953: }
! 954: /* }}} */
! 955:
! 956:
! 957: /* {{{ mysqlnd_stmt::use_result */
! 958: static MYSQLND_RES *
! 959: MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
! 960: {
! 961: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 962: MYSQLND_RES *result;
! 963: MYSQLND * conn;
! 964:
! 965: DBG_ENTER("mysqlnd_stmt::use_result");
! 966: if (!stmt || !stmt->conn || !stmt->result) {
! 967: DBG_RETURN(NULL);
! 968: }
! 969: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 970:
! 971: conn = stmt->conn;
! 972:
! 973: if (!stmt->field_count ||
! 974: (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) ||
! 975: (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) ||
! 976: (stmt->state != MYSQLND_STMT_WAITING_USE_OR_STORE))
! 977: {
! 978: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
! 979: UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 980: DBG_ERR("command out of sync");
! 981: DBG_RETURN(NULL);
! 982: }
! 983:
! 984: SET_EMPTY_ERROR(stmt->error_info);
! 985:
! 986: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_PS_UNBUFFERED_SETS);
! 987: result = stmt->result;
! 988:
! 989: DBG_INF_FMT("%scursor exists", stmt->cursor_exists? "":"no ");
! 990: result->m.use_result(stmt->result, TRUE TSRMLS_CC);
! 991: result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
! 992: mysqlnd_stmt_fetch_row_unbuffered;
! 993: stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
! 994:
! 995: DBG_INF_FMT("%p", result);
! 996: DBG_RETURN(result);
! 997: }
! 998: /* }}} */
! 999:
! 1000:
! 1001: #define STMT_ID_LENGTH 4
! 1002:
! 1003: /* {{{ mysqlnd_fetch_row_cursor */
! 1004: enum_func_status
! 1005: mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
! 1006: {
! 1007: enum_func_status ret;
! 1008: MYSQLND_STMT * s = (MYSQLND_STMT *) param;
! 1009: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1010: zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
! 1011: MYSQLND_PACKET_ROW * row_packet;
! 1012:
! 1013: DBG_ENTER("mysqlnd_fetch_stmt_row_cursor");
! 1014:
! 1015: if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) {
! 1016: DBG_ERR("no statement");
! 1017: DBG_RETURN(FAIL);
! 1018: }
! 1019:
! 1020: DBG_INF_FMT("stmt=%lu flags=%u", stmt->stmt_id, flags);
! 1021:
! 1022: if (stmt->state < MYSQLND_STMT_USER_FETCHING) {
! 1023: /* Only initted - error */
! 1024: SET_CLIENT_ERROR(stmt->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
! 1025: mysqlnd_out_of_sync);
! 1026: DBG_ERR("command out of sync");
! 1027: DBG_RETURN(FAIL);
! 1028: }
! 1029: if (!(row_packet = result->row_packet)) {
! 1030: DBG_RETURN(FAIL);
! 1031: }
! 1032:
! 1033: SET_EMPTY_ERROR(stmt->error_info);
! 1034: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1035:
! 1036: int4store(buf, stmt->stmt_id);
! 1037: int4store(buf + STMT_ID_LENGTH, 1); /* for now fetch only one row */
! 1038:
! 1039: if (FAIL == stmt->conn->m->simple_command(stmt->conn, COM_STMT_FETCH, (char *)buf, sizeof(buf),
! 1040: PROT_LAST /* we will handle the response packet*/,
! 1041: FALSE, TRUE TSRMLS_CC)) {
! 1042: stmt->error_info = stmt->conn->error_info;
! 1043: DBG_RETURN(FAIL);
! 1044: }
! 1045:
! 1046: row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE;
! 1047:
! 1048: if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
! 1049: unsigned int i, field_count = result->field_count;
! 1050:
! 1051: DBG_INF_FMT("skip_extraction=%u", row_packet->skip_extraction);
! 1052: if (!row_packet->skip_extraction) {
! 1053: result->m.unbuffered_free_last_data(result TSRMLS_CC);
! 1054:
! 1055: DBG_INF("extracting data");
! 1056: result->unbuf->last_row_data = row_packet->fields;
! 1057: result->unbuf->last_row_buffer = row_packet->row_buffer;
! 1058: row_packet->fields = NULL;
! 1059: row_packet->row_buffer = NULL;
! 1060:
! 1061: if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
! 1062: result->unbuf->last_row_data,
! 1063: row_packet->field_count,
! 1064: row_packet->fields_metadata,
! 1065: FALSE,
! 1066: result->conn->options.numeric_and_datetime_as_unicode,
! 1067: result->conn->options.int_and_float_native,
! 1068: result->conn->stats TSRMLS_CC))
! 1069: {
! 1070: DBG_RETURN(FAIL);
! 1071: }
! 1072:
! 1073: /* If no result bind, do nothing. We consumed the data */
! 1074: for (i = 0; i < field_count; i++) {
! 1075: if (stmt->result_bind[i].bound == TRUE) {
! 1076: zval *data = result->unbuf->last_row_data[i];
! 1077: /*
! 1078: stmt->result_bind[i].zv has been already destructed
! 1079: in result->m.unbuffered_free_last_data()
! 1080: */
! 1081: #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 1082: zval_dtor(stmt->result_bind[i].zv);
! 1083: #endif
! 1084: DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
! 1085: Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
! 1086: if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
! 1087: if ((Z_TYPE_P(data) == IS_STRING
! 1088: #if MYSQLND_UNICODE
! 1089: || Z_TYPE_P(data) == IS_UNICODE
! 1090: #endif
! 1091: )
! 1092: && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data)))
! 1093: {
! 1094: result->meta->fields[i].max_length = Z_STRLEN_P(data);
! 1095: }
! 1096: stmt->result_bind[i].zv->value = data->value;
! 1097: /* copied data, thus also the ownership. Thus null data */
! 1098: ZVAL_NULL(data);
! 1099: }
! 1100: }
! 1101: }
! 1102: } else {
! 1103: DBG_INF("skipping extraction");
! 1104: /*
! 1105: Data has been allocated and usually result->m.unbuffered_free_last_data()
! 1106: frees it but we can't call this function as it will cause problems with
! 1107: the bound variables. Thus we need to do part of what it does or Zend will
! 1108: report leaks.
! 1109: */
! 1110: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
! 1111: row_packet->row_buffer = NULL;
! 1112: }
! 1113: /* We asked for one row, the next one should be EOF, eat it */
! 1114: ret = PACKET_READ(row_packet, result->conn);
! 1115: if (row_packet->row_buffer) {
! 1116: row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC);
! 1117: row_packet->row_buffer = NULL;
! 1118: }
! 1119: MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR);
! 1120:
! 1121: result->unbuf->row_count++;
! 1122: *fetched_anything = TRUE;
! 1123: } else {
! 1124: *fetched_anything = FALSE;
! 1125:
! 1126: stmt->upsert_status.warning_count =
! 1127: stmt->conn->upsert_status.warning_count =
! 1128: row_packet->warning_count;
! 1129:
! 1130: stmt->upsert_status.server_status =
! 1131: stmt->conn->upsert_status.server_status =
! 1132: row_packet->server_status;
! 1133:
! 1134: result->unbuf->eof_reached = row_packet->eof;
! 1135: }
! 1136: stmt->upsert_status.warning_count =
! 1137: stmt->conn->upsert_status.warning_count =
! 1138: row_packet->warning_count;
! 1139: stmt->upsert_status.server_status =
! 1140: stmt->conn->upsert_status.server_status =
! 1141: row_packet->server_status;
! 1142:
! 1143: DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u",
! 1144: ret == PASS? "PASS":"FAIL", *fetched_anything,
! 1145: row_packet->server_status, row_packet->warning_count,
! 1146: result->unbuf->eof_reached);
! 1147: DBG_RETURN(ret);
! 1148: }
! 1149: /* }}} */
! 1150:
! 1151:
! 1152: /* {{{ mysqlnd_stmt::fetch */
! 1153: static enum_func_status
! 1154: MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC)
! 1155: {
! 1156: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1157: enum_func_status ret;
! 1158: DBG_ENTER("mysqlnd_stmt::fetch");
! 1159: if (!stmt || !stmt->conn) {
! 1160: DBG_RETURN(FAIL);
! 1161: }
! 1162: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 1163:
! 1164: if (!stmt->result ||
! 1165: stmt->state < MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 1166: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 1167:
! 1168: DBG_ERR("command out of sync");
! 1169: DBG_RETURN(FAIL);
! 1170: } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 1171: /* Execute only once. We have to free the previous contents of user's bound vars */
! 1172:
! 1173: stmt->default_rset_handler(s TSRMLS_CC);
! 1174: }
! 1175: stmt->state = MYSQLND_STMT_USER_FETCHING;
! 1176:
! 1177: SET_EMPTY_ERROR(stmt->error_info);
! 1178: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1179:
! 1180: DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once);
! 1181: /*
! 1182: The user might have not bound any variables for result.
! 1183: Do the binding once she does it.
! 1184: */
! 1185: if (stmt->result_bind && !stmt->result_zvals_separated_once) {
! 1186: unsigned int i;
! 1187: /*
! 1188: mysqlnd_stmt_store_result() has been called free the bind
! 1189: variables to prevent leaking of their previous content.
! 1190: */
! 1191: for (i = 0; i < stmt->result->field_count; i++) {
! 1192: if (stmt->result_bind[i].bound == TRUE) {
! 1193: zval_dtor(stmt->result_bind[i].zv);
! 1194: ZVAL_NULL(stmt->result_bind[i].zv);
! 1195: }
! 1196: }
! 1197: stmt->result_zvals_separated_once = TRUE;
! 1198: }
! 1199:
! 1200: ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC);
! 1201: DBG_RETURN(ret);
! 1202: }
! 1203: /* }}} */
! 1204:
! 1205:
! 1206: /* {{{ mysqlnd_stmt::reset */
! 1207: static enum_func_status
! 1208: MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC)
! 1209: {
! 1210: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1211: enum_func_status ret = PASS;
! 1212: zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
! 1213:
! 1214: DBG_ENTER("mysqlnd_stmt::reset");
! 1215: if (!stmt || !stmt->conn) {
! 1216: DBG_RETURN(FAIL);
! 1217: }
! 1218: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 1219:
! 1220: SET_EMPTY_ERROR(stmt->error_info);
! 1221: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1222:
! 1223: if (stmt->stmt_id) {
! 1224: MYSQLND * conn = stmt->conn;
! 1225: if (stmt->param_bind) {
! 1226: unsigned int i;
! 1227: DBG_INF("resetting long data");
! 1228: /* Reset Long Data */
! 1229: for (i = 0; i < stmt->param_count; i++) {
! 1230: if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
! 1231: stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
! 1232: }
! 1233: }
! 1234: }
! 1235:
! 1236: /*
! 1237: If the user decided to close the statement right after execute()
! 1238: We have to call the appropriate use_result() or store_result() and
! 1239: clean.
! 1240: */
! 1241: do {
! 1242: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 1243: DBG_INF("fetching result set header");
! 1244: stmt->default_rset_handler(s TSRMLS_CC);
! 1245: stmt->state = MYSQLND_STMT_USER_FETCHING;
! 1246: }
! 1247:
! 1248: if (stmt->result) {
! 1249: DBG_INF("skipping result");
! 1250: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
! 1251: }
! 1252: } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
! 1253:
! 1254: /*
! 1255: Don't free now, let the result be usable. When the stmt will again be
! 1256: executed then the result set will be cleaned, the bound variables will
! 1257: be separated before that.
! 1258: */
! 1259:
! 1260: int4store(cmd_buf, stmt->stmt_id);
! 1261: if (CONN_GET_STATE(conn) == CONN_READY &&
! 1262: FAIL == (ret = conn->m->simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
! 1263: sizeof(cmd_buf), PROT_OK_PACKET,
! 1264: FALSE, TRUE TSRMLS_CC))) {
! 1265: stmt->error_info = conn->error_info;
! 1266: }
! 1267: stmt->upsert_status = conn->upsert_status;
! 1268:
! 1269: stmt->state = MYSQLND_STMT_PREPARED;
! 1270: }
! 1271: DBG_INF(ret == PASS? "PASS":"FAIL");
! 1272: DBG_RETURN(ret);
! 1273: }
! 1274: /* }}} */
! 1275:
! 1276:
! 1277: /* {{{ mysqlnd_stmt::send_long_data */
! 1278: static enum_func_status
! 1279: MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no,
! 1280: const char * const data, unsigned long length TSRMLS_DC)
! 1281: {
! 1282: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1283: enum_func_status ret = FAIL;
! 1284: MYSQLND * conn;
! 1285: zend_uchar *cmd_buf;
! 1286: enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA;
! 1287:
! 1288: DBG_ENTER("mysqlnd_stmt::send_long_data");
! 1289: if (!stmt || !stmt->conn) {
! 1290: DBG_RETURN(FAIL);
! 1291: }
! 1292: DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length);
! 1293:
! 1294: conn = stmt->conn;
! 1295:
! 1296: SET_EMPTY_ERROR(stmt->error_info);
! 1297: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1298:
! 1299: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1300: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1301: DBG_ERR("not prepared");
! 1302: DBG_RETURN(FAIL);
! 1303: }
! 1304: if (!stmt->param_bind) {
! 1305: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
! 1306: DBG_ERR("command out of sync");
! 1307: DBG_RETURN(FAIL);
! 1308: }
! 1309:
! 1310: if (param_no >= stmt->param_count) {
! 1311: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
! 1312: DBG_ERR("invalid param_no");
! 1313: DBG_RETURN(FAIL);
! 1314: }
! 1315: if (stmt->param_bind[param_no].type != MYSQL_TYPE_LONG_BLOB) {
! 1316: SET_STMT_ERROR(stmt, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, mysqlnd_not_bound_as_blob);
! 1317: DBG_ERR("param_no is not of a blob type");
! 1318: DBG_RETURN(FAIL);
! 1319: }
! 1320:
! 1321: /*
! 1322: XXX: Unfortunately we have to allocate additional buffer to be able the
! 1323: additional data, which is like a header inside the payload.
! 1324: This should be optimised, but it will be a pervasive change, so
! 1325: conn->m->simple_command() will accept not a buffer, but actually MYSQLND_STRING*
! 1326: terminated by NULL, to send. If the strings are not big, we can collapse them
! 1327: on the buffer every connection has, but otherwise we will just send them
! 1328: one by one to the wire.
! 1329: */
! 1330:
! 1331: if (CONN_GET_STATE(conn) == CONN_READY) {
! 1332: size_t packet_len;
! 1333: cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length);
! 1334: if (cmd_buf) {
! 1335: stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED;
! 1336:
! 1337: int4store(cmd_buf, stmt->stmt_id);
! 1338: int2store(cmd_buf + STMT_ID_LENGTH, param_no);
! 1339: memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length);
! 1340:
! 1341: /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/
! 1342: ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, PROT_LAST , FALSE, TRUE TSRMLS_CC);
! 1343: mnd_efree(cmd_buf);
! 1344: if (FAIL == ret) {
! 1345: stmt->error_info = conn->error_info;
! 1346: }
! 1347: } else {
! 1348: ret = FAIL;
! 1349: SET_OOM_ERROR(stmt->error_info);
! 1350: SET_OOM_ERROR(conn->error_info);
! 1351: }
! 1352: /*
! 1353: Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not
! 1354: sent response packets. According to documentation the only way to get an error
! 1355: is to have out-of-memory on the server-side. However, that's not true, as if
! 1356: max_allowed_packet_size is smaller than the chunk being sent to the server, the
! 1357: latter will complain with an error message. However, normally we don't expect
! 1358: an error message, thus we continue. When sending the next command, which expects
! 1359: response we will read the unexpected data and error message will look weird.
! 1360: Therefore we do non-blocking read to clean the line, if there is a need.
! 1361: Nevertheless, there is a built-in protection when sending a command packet, that
! 1362: checks if the line is clear - useful for debug purposes and to be switched off
! 1363: in release builds.
! 1364:
! 1365: Maybe we can make it automatic by checking what's the value of
! 1366: max_allowed_packet_size on the server and resending the data.
! 1367: */
! 1368: #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
! 1369: #if HAVE_USLEEP && !defined(PHP_WIN32)
! 1370: usleep(120000);
! 1371: #endif
! 1372: if ((packet_len = conn->net->m.consume_uneaten_data(conn->net, cmd TSRMLS_CC))) {
! 1373: php_error_docref(NULL TSRMLS_CC, E_WARNING, "There was an error "
! 1374: "while sending long data. Probably max_allowed_packet_size "
! 1375: "is smaller than the data. You have to increase it or send "
! 1376: "smaller chunks of data. Answer was "MYSQLND_SZ_T_SPEC" bytes long.", packet_len);
! 1377: SET_STMT_ERROR(stmt, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE,
! 1378: "Server responded to COM_STMT_SEND_LONG_DATA.");
! 1379: ret = FAIL;
! 1380: }
! 1381: #endif
! 1382: }
! 1383:
! 1384: DBG_INF(ret == PASS? "PASS":"FAIL");
! 1385: DBG_RETURN(ret);
! 1386: }
! 1387: /* }}} */
! 1388:
! 1389:
! 1390: /* {{{ mysqlnd_stmt::bind_parameters */
! 1391: static enum_func_status
! 1392: MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC)
! 1393: {
! 1394: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1395: DBG_ENTER("mysqlnd_stmt::bind_param");
! 1396: if (!stmt || !stmt->conn) {
! 1397: DBG_RETURN(FAIL);
! 1398: }
! 1399: DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
! 1400:
! 1401: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1402: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1403: DBG_ERR("not prepared");
! 1404: if (param_bind) {
! 1405: s->m->free_parameter_bind(s, param_bind TSRMLS_CC);
! 1406: }
! 1407: DBG_RETURN(FAIL);
! 1408: }
! 1409:
! 1410: SET_EMPTY_ERROR(stmt->error_info);
! 1411: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1412:
! 1413: if (stmt->param_count) {
! 1414: unsigned int i = 0;
! 1415:
! 1416: if (!param_bind) {
! 1417: SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported");
! 1418: DBG_ERR("Re-binding (still) not supported");
! 1419: DBG_RETURN(FAIL);
! 1420: } else if (stmt->param_bind) {
! 1421: DBG_INF("Binding");
! 1422: /*
! 1423: There is already result bound.
! 1424: Forbid for now re-binding!!
! 1425: */
! 1426: for (i = 0; i < stmt->param_count; i++) {
! 1427: /*
! 1428: We may have the last reference, then call zval_ptr_dtor()
! 1429: or we may leak memory.
! 1430: Switching from bind_one_parameter to bind_parameters may result in zv being NULL
! 1431: */
! 1432: if (stmt->param_bind[i].zv) {
! 1433: zval_ptr_dtor(&stmt->param_bind[i].zv);
! 1434: }
! 1435: }
! 1436: if (stmt->param_bind != param_bind) {
! 1437: s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
! 1438: }
! 1439: }
! 1440:
! 1441: stmt->param_bind = param_bind;
! 1442: for (i = 0; i < stmt->param_count; i++) {
! 1443: /* The client will use stmt_send_long_data */
! 1444: DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type);
! 1445: /* Prevent from freeing */
! 1446: /* Don't update is_ref, or we will leak during conversion */
! 1447: Z_ADDREF_P(stmt->param_bind[i].zv);
! 1448: stmt->param_bind[i].flags = 0;
! 1449: if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) {
! 1450: stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
! 1451: }
! 1452: }
! 1453: stmt->send_types_to_server = 1;
! 1454: }
! 1455: DBG_INF("PASS");
! 1456: DBG_RETURN(PASS);
! 1457: }
! 1458: /* }}} */
! 1459:
! 1460:
! 1461: /* {{{ mysqlnd_stmt::bind_one_parameter */
! 1462: static enum_func_status
! 1463: MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no,
! 1464: zval * const zv, zend_uchar type TSRMLS_DC)
! 1465: {
! 1466: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1467: DBG_ENTER("mysqlnd_stmt::bind_one_parameter");
! 1468: if (!stmt || !stmt->conn) {
! 1469: DBG_RETURN(FAIL);
! 1470: }
! 1471: DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u",
! 1472: stmt->stmt_id, param_no, stmt->param_count, type);
! 1473:
! 1474: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1475: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1476: DBG_ERR("not prepared");
! 1477: DBG_RETURN(FAIL);
! 1478: }
! 1479:
! 1480: if (param_no >= stmt->param_count) {
! 1481: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
! 1482: DBG_ERR("invalid param_no");
! 1483: DBG_RETURN(FAIL);
! 1484: }
! 1485: SET_EMPTY_ERROR(stmt->error_info);
! 1486: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1487:
! 1488: if (stmt->param_count) {
! 1489: if (!stmt->param_bind) {
! 1490: stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND));
! 1491: if (!stmt->param_bind) {
! 1492: DBG_RETURN(FAIL);
! 1493: }
! 1494: }
! 1495:
! 1496: /* Prevent from freeing */
! 1497: /* Don't update is_ref, or we will leak during conversion */
! 1498: Z_ADDREF_P(zv);
! 1499: DBG_INF("Binding");
! 1500: /* Release what we had, if we had */
! 1501: if (stmt->param_bind[param_no].zv) {
! 1502: zval_ptr_dtor(&stmt->param_bind[param_no].zv);
! 1503: }
! 1504: if (type == MYSQL_TYPE_LONG_BLOB) {
! 1505: /* The client will use stmt_send_long_data */
! 1506: stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
! 1507: }
! 1508: stmt->param_bind[param_no].zv = zv;
! 1509: stmt->param_bind[param_no].type = type;
! 1510:
! 1511: stmt->send_types_to_server = 1;
! 1512: }
! 1513: DBG_INF("PASS");
! 1514: DBG_RETURN(PASS);
! 1515: }
! 1516: /* }}} */
! 1517:
! 1518:
! 1519: /* {{{ mysqlnd_stmt::refresh_bind_param */
! 1520: static enum_func_status
! 1521: MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC)
! 1522: {
! 1523: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1524: DBG_ENTER("mysqlnd_stmt::refresh_bind_param");
! 1525: if (!stmt || !stmt->conn) {
! 1526: DBG_RETURN(FAIL);
! 1527: }
! 1528: DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count);
! 1529:
! 1530: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1531: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1532: DBG_ERR("not prepared");
! 1533: DBG_RETURN(FAIL);
! 1534: }
! 1535:
! 1536: SET_EMPTY_ERROR(stmt->error_info);
! 1537: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1538:
! 1539: if (stmt->param_count) {
! 1540: stmt->send_types_to_server = 1;
! 1541: }
! 1542: DBG_INF("PASS");
! 1543: DBG_RETURN(PASS);
! 1544: }
! 1545: /* }}} */
! 1546:
! 1547:
! 1548: /* {{{ mysqlnd_stmt::bind_result */
! 1549: static enum_func_status
! 1550: MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s,
! 1551: MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC)
! 1552: {
! 1553: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1554: DBG_ENTER("mysqlnd_stmt::bind_result");
! 1555: if (!stmt || !stmt->conn) {
! 1556: DBG_RETURN(FAIL);
! 1557: }
! 1558: DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
! 1559:
! 1560: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1561: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1562: if (result_bind) {
! 1563: s->m->free_result_bind(s, result_bind TSRMLS_CC);
! 1564: }
! 1565: DBG_ERR("not prepared");
! 1566: DBG_RETURN(FAIL);
! 1567: }
! 1568:
! 1569: SET_EMPTY_ERROR(stmt->error_info);
! 1570: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1571:
! 1572: if (stmt->field_count) {
! 1573: unsigned int i = 0;
! 1574:
! 1575: if (!result_bind) {
! 1576: DBG_ERR("no result bind passed");
! 1577: DBG_RETURN(FAIL);
! 1578: }
! 1579:
! 1580: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
! 1581: stmt->result_zvals_separated_once = FALSE;
! 1582: stmt->result_bind = result_bind;
! 1583: for (i = 0; i < stmt->field_count; i++) {
! 1584: /* Prevent from freeing */
! 1585: Z_ADDREF_P(stmt->result_bind[i].zv);
! 1586: DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv));
! 1587: /*
! 1588: Don't update is_ref !!! it's not our job
! 1589: Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
! 1590: will fail.
! 1591: */
! 1592: stmt->result_bind[i].bound = TRUE;
! 1593: }
! 1594: } else if (result_bind) {
! 1595: s->m->free_result_bind(s, result_bind TSRMLS_CC);
! 1596: }
! 1597: DBG_INF("PASS");
! 1598: DBG_RETURN(PASS);
! 1599: }
! 1600: /* }}} */
! 1601:
! 1602:
! 1603: /* {{{ mysqlnd_stmt::bind_result */
! 1604: static enum_func_status
! 1605: MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
! 1606: {
! 1607: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1608: DBG_ENTER("mysqlnd_stmt::bind_result");
! 1609: if (!stmt || !stmt->conn) {
! 1610: DBG_RETURN(FAIL);
! 1611: }
! 1612: DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
! 1613:
! 1614: if (stmt->state < MYSQLND_STMT_PREPARED) {
! 1615: SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
! 1616: DBG_ERR("not prepared");
! 1617: DBG_RETURN(FAIL);
! 1618: }
! 1619:
! 1620: if (param_no >= stmt->field_count) {
! 1621: SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
! 1622: DBG_ERR("invalid param_no");
! 1623: DBG_RETURN(FAIL);
! 1624: }
! 1625:
! 1626: SET_EMPTY_ERROR(stmt->error_info);
! 1627: SET_EMPTY_ERROR(stmt->conn->error_info);
! 1628:
! 1629: if (stmt->field_count) {
! 1630: mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC);
! 1631: /* Guaranteed is that stmt->result_bind is NULL */
! 1632: if (!stmt->result_bind) {
! 1633: stmt->result_bind = mnd_ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND));
! 1634: } else {
! 1635: stmt->result_bind = mnd_erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND));
! 1636: }
! 1637: if (!stmt->result_bind) {
! 1638: DBG_RETURN(FAIL);
! 1639: }
! 1640: ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv);
! 1641: /*
! 1642: Don't update is_ref !!! it's not our job
! 1643: Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt
! 1644: will fail.
! 1645: */
! 1646: stmt->result_bind[param_no].bound = TRUE;
! 1647: }
! 1648: DBG_INF("PASS");
! 1649: DBG_RETURN(PASS);
! 1650: }
! 1651: /* }}} */
! 1652:
! 1653:
! 1654: /* {{{ mysqlnd_stmt::insert_id */
! 1655: static uint64_t
! 1656: MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1657: {
! 1658: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1659: return stmt? stmt->upsert_status.last_insert_id : 0;
! 1660: }
! 1661: /* }}} */
! 1662:
! 1663:
! 1664: /* {{{ mysqlnd_stmt::affected_rows */
! 1665: static uint64_t
! 1666: MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1667: {
! 1668: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1669: return stmt? stmt->upsert_status.affected_rows : 0;
! 1670: }
! 1671: /* }}} */
! 1672:
! 1673:
! 1674: /* {{{ mysqlnd_stmt::num_rows */
! 1675: static uint64_t
! 1676: MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1677: {
! 1678: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1679: return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0;
! 1680: }
! 1681: /* }}} */
! 1682:
! 1683:
! 1684: /* {{{ mysqlnd_stmt::warning_count */
! 1685: static unsigned int
! 1686: MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1687: {
! 1688: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1689: return stmt? stmt->upsert_status.warning_count : 0;
! 1690: }
! 1691: /* }}} */
! 1692:
! 1693:
! 1694: /* {{{ mysqlnd_stmt::server_status */
! 1695: static unsigned int
! 1696: MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1697: {
! 1698: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1699: return stmt? stmt->upsert_status.server_status : 0;
! 1700: }
! 1701: /* }}} */
! 1702:
! 1703:
! 1704: /* {{{ mysqlnd_stmt::field_count */
! 1705: static unsigned int
! 1706: MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1707: {
! 1708: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1709: return stmt? stmt->field_count : 0;
! 1710: }
! 1711: /* }}} */
! 1712:
! 1713:
! 1714: /* {{{ mysqlnd_stmt::param_count */
! 1715: static unsigned int
! 1716: MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1717: {
! 1718: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1719: return stmt? stmt->param_count : 0;
! 1720: }
! 1721: /* }}} */
! 1722:
! 1723:
! 1724: /* {{{ mysqlnd_stmt::errno */
! 1725: static unsigned int
! 1726: MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1727: {
! 1728: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1729: return stmt? stmt->error_info.error_no : 0;
! 1730: }
! 1731: /* }}} */
! 1732:
! 1733:
! 1734: /* {{{ mysqlnd_stmt::error */
! 1735: static const char *
! 1736: MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1737: {
! 1738: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1739: return stmt? stmt->error_info.error : 0;
! 1740: }
! 1741: /* }}} */
! 1742:
! 1743:
! 1744: /* {{{ mysqlnd_stmt::sqlstate */
! 1745: static const char *
! 1746: MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC)
! 1747: {
! 1748: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1749: return stmt && stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;
! 1750: }
! 1751: /* }}} */
! 1752:
! 1753:
! 1754: /* {{{ mysqlnd_stmt::data_seek */
! 1755: static enum_func_status
! 1756: MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC)
! 1757: {
! 1758: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1759: return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL;
! 1760: }
! 1761: /* }}} */
! 1762:
! 1763:
! 1764: /* {{{ mysqlnd_stmt::param_metadata */
! 1765: static MYSQLND_RES *
! 1766: MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
! 1767: {
! 1768: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1769: if (!stmt || !stmt->param_count) {
! 1770: return NULL;
! 1771: }
! 1772: return NULL;
! 1773: }
! 1774: /* }}} */
! 1775:
! 1776:
! 1777: /* {{{ mysqlnd_stmt::result_metadata */
! 1778: static MYSQLND_RES *
! 1779: MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
! 1780: {
! 1781: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1782: MYSQLND_RES *result;
! 1783:
! 1784: DBG_ENTER("mysqlnd_stmt::result_metadata");
! 1785: if (!stmt) {
! 1786: DBG_RETURN(NULL);
! 1787: }
! 1788: DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count);
! 1789:
! 1790: if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) {
! 1791: DBG_INF("NULL");
! 1792: DBG_RETURN(NULL);
! 1793: }
! 1794:
! 1795: if (stmt->update_max_length && stmt->result->stored_data) {
! 1796: /* stored result, we have to update the max_length before we clone the meta data :( */
! 1797: stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC);
! 1798: }
! 1799: /*
! 1800: TODO: This implementation is kind of a hack,
! 1801: find a better way to do it. In different functions I have put
! 1802: fuses to check for result->m.fetch_row() being NULL. This should
! 1803: be handled in a better way.
! 1804:
! 1805: In the meantime we don't need a zval cache reference for this fake
! 1806: result set, so we don't get one.
! 1807: */
! 1808: do {
! 1809: result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC);
! 1810: if (!result) {
! 1811: break;
! 1812: }
! 1813: result->type = MYSQLND_RES_NORMAL;
! 1814: result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
! 1815: result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
! 1816: if (!result->unbuf) {
! 1817: break;
! 1818: }
! 1819: result->unbuf->eof_reached = TRUE;
! 1820: result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC);
! 1821: if (!result->meta) {
! 1822: break;
! 1823: }
! 1824:
! 1825: DBG_INF_FMT("result=%p", result);
! 1826: DBG_RETURN(result);
! 1827: } while (0);
! 1828:
! 1829: SET_OOM_ERROR(stmt->conn->error_info);
! 1830: if (result) {
! 1831: result->m.free_result(result, TRUE TSRMLS_CC);
! 1832: }
! 1833: DBG_RETURN(NULL);
! 1834: }
! 1835: /* }}} */
! 1836:
! 1837:
! 1838: /* {{{ mysqlnd_stmt::attr_set */
! 1839: static enum_func_status
! 1840: MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s,
! 1841: enum mysqlnd_stmt_attr attr_type,
! 1842: const void * const value TSRMLS_DC)
! 1843: {
! 1844: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1845: DBG_ENTER("mysqlnd_stmt::attr_set");
! 1846: if (!stmt) {
! 1847: DBG_RETURN(FAIL);
! 1848: }
! 1849: DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
! 1850:
! 1851: switch (attr_type) {
! 1852: case STMT_ATTR_UPDATE_MAX_LENGTH:{
! 1853: zend_uchar bval = *(zend_uchar *) value;
! 1854: /*
! 1855: XXX : libmysql uses my_bool, but mysqli uses ulong as storage on the stack
! 1856: and mysqlnd won't be used out of the scope of PHP -> use ulong.
! 1857: */
! 1858: stmt->update_max_length = bval? TRUE:FALSE;
! 1859: break;
! 1860: }
! 1861: case STMT_ATTR_CURSOR_TYPE: {
! 1862: unsigned int ival = *(unsigned int *) value;
! 1863: if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) {
! 1864: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
! 1865: DBG_INF("FAIL");
! 1866: DBG_RETURN(FAIL);
! 1867: }
! 1868: stmt->flags = ival;
! 1869: break;
! 1870: }
! 1871: case STMT_ATTR_PREFETCH_ROWS: {
! 1872: unsigned int ival = *(unsigned int *) value;
! 1873: if (ival == 0) {
! 1874: ival = MYSQLND_DEFAULT_PREFETCH_ROWS;
! 1875: } else if (ival > 1) {
! 1876: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
! 1877: DBG_INF("FAIL");
! 1878: DBG_RETURN(FAIL);
! 1879: }
! 1880: stmt->prefetch_rows = ival;
! 1881: break;
! 1882: }
! 1883: default:
! 1884: SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented");
! 1885: DBG_RETURN(FAIL);
! 1886: }
! 1887: DBG_INF("PASS");
! 1888: DBG_RETURN(PASS);
! 1889: }
! 1890: /* }}} */
! 1891:
! 1892:
! 1893: /* {{{ mysqlnd_stmt::attr_get */
! 1894: static enum_func_status
! 1895: MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s,
! 1896: enum mysqlnd_stmt_attr attr_type,
! 1897: void * const value TSRMLS_DC)
! 1898: {
! 1899: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1900: DBG_ENTER("mysqlnd_stmt::attr_set");
! 1901: if (!stmt) {
! 1902: DBG_RETURN(FAIL);
! 1903: }
! 1904: DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type);
! 1905:
! 1906: switch (attr_type) {
! 1907: case STMT_ATTR_UPDATE_MAX_LENGTH:
! 1908: *(zend_bool *) value= stmt->update_max_length;
! 1909: break;
! 1910: case STMT_ATTR_CURSOR_TYPE:
! 1911: *(unsigned long *) value= stmt->flags;
! 1912: break;
! 1913: case STMT_ATTR_PREFETCH_ROWS:
! 1914: *(unsigned long *) value= stmt->prefetch_rows;
! 1915: break;
! 1916: default:
! 1917: DBG_RETURN(FAIL);
! 1918: }
! 1919: DBG_INF_FMT("value=%lu", value);
! 1920: DBG_RETURN(PASS);
! 1921: }
! 1922: /* }}} */
! 1923:
! 1924: /* free_result() doesn't actually free stmt->result but only the buffers */
! 1925: /* {{{ mysqlnd_stmt::free_result */
! 1926: static enum_func_status
! 1927: MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC)
! 1928: {
! 1929: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1930: DBG_ENTER("mysqlnd_stmt::free_result");
! 1931: if (!stmt || !stmt->conn) {
! 1932: DBG_RETURN(FAIL);
! 1933: }
! 1934: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 1935:
! 1936: if (!stmt->result) {
! 1937: DBG_INF("no result");
! 1938: DBG_RETURN(PASS);
! 1939: }
! 1940:
! 1941: /*
! 1942: If right after execute() we have to call the appropriate
! 1943: use_result() or store_result() and clean.
! 1944: */
! 1945: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 1946: DBG_INF("fetching result set header");
! 1947: /* Do implicit use_result and then flush the result */
! 1948: stmt->default_rset_handler = s->m->use_result;
! 1949: stmt->default_rset_handler(s TSRMLS_CC);
! 1950: }
! 1951:
! 1952: if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 1953: DBG_INF("skipping result");
! 1954: /* Flush if anything is left and unbuffered set */
! 1955: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
! 1956: /*
! 1957: Separate the bound variables, which point to the result set, then
! 1958: destroy the set.
! 1959: */
! 1960: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
! 1961:
! 1962: /* Now we can destroy the result set */
! 1963: stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
! 1964: }
! 1965:
! 1966: if (stmt->state > MYSQLND_STMT_PREPARED) {
! 1967: /* As the buffers have been freed, we should go back to PREPARED */
! 1968: stmt->state = MYSQLND_STMT_PREPARED;
! 1969: }
! 1970:
! 1971: /* Line is free! */
! 1972: CONN_SET_STATE(stmt->conn, CONN_READY);
! 1973:
! 1974: DBG_RETURN(PASS);
! 1975: }
! 1976: /* }}} */
! 1977:
! 1978:
! 1979: /* {{{ mysqlnd_stmt_separate_result_bind */
! 1980: static void
! 1981: mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC)
! 1982: {
! 1983: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 1984: unsigned int i;
! 1985:
! 1986: DBG_ENTER("mysqlnd_stmt_separate_result_bind");
! 1987: if (!stmt) {
! 1988: DBG_VOID_RETURN;
! 1989: }
! 1990: DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u",
! 1991: stmt->stmt_id, stmt->result_bind, stmt->field_count);
! 1992:
! 1993: if (!stmt->result_bind) {
! 1994: DBG_VOID_RETURN;
! 1995: }
! 1996:
! 1997: /*
! 1998: Because only the bound variables can point to our internal buffers, then
! 1999: separate or free only them. Free is possible because the user could have
! 2000: lost reference.
! 2001: */
! 2002: for (i = 0; i < stmt->field_count; i++) {
! 2003: /* Let's try with no cache */
! 2004: if (stmt->result_bind[i].bound == TRUE) {
! 2005: DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv));
! 2006: /*
! 2007: We have to separate the actual zval value of the bound
! 2008: variable from our allocated zvals or we will face double-free
! 2009: */
! 2010: if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) {
! 2011: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 2012: zval_copy_ctor(stmt->result_bind[i].zv);
! 2013: #endif
! 2014: zval_ptr_dtor(&stmt->result_bind[i].zv);
! 2015: } else {
! 2016: /*
! 2017: If it is a string, what is pointed will be freed
! 2018: later in free_result(). We need to remove the variable to
! 2019: which the user has lost reference.
! 2020: */
! 2021: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 2022: ZVAL_NULL(stmt->result_bind[i].zv);
! 2023: #endif
! 2024: zval_ptr_dtor(&stmt->result_bind[i].zv);
! 2025: }
! 2026: }
! 2027: }
! 2028: s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC);
! 2029: stmt->result_bind = NULL;
! 2030:
! 2031: DBG_VOID_RETURN;
! 2032: }
! 2033: /* }}} */
! 2034:
! 2035:
! 2036: /* {{{ mysqlnd_stmt_separate_one_result_bind */
! 2037: static void
! 2038: mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC)
! 2039: {
! 2040: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2041: DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
! 2042: if (!stmt) {
! 2043: DBG_VOID_RETURN;
! 2044: }
! 2045: DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u",
! 2046: stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
! 2047:
! 2048: if (!stmt->result_bind) {
! 2049: DBG_VOID_RETURN;
! 2050: }
! 2051:
! 2052: /*
! 2053: Because only the bound variables can point to our internal buffers, then
! 2054: separate or free only them. Free is possible because the user could have
! 2055: lost reference.
! 2056: */
! 2057: /* Let's try with no cache */
! 2058: if (stmt->result_bind[param_no].bound == TRUE) {
! 2059: DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv));
! 2060: /*
! 2061: We have to separate the actual zval value of the bound
! 2062: variable from our allocated zvals or we will face double-free
! 2063: */
! 2064: if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
! 2065: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 2066: zval_copy_ctor(stmt->result_bind[param_no].zv);
! 2067: #endif
! 2068: zval_ptr_dtor(&stmt->result_bind[param_no].zv);
! 2069: } else {
! 2070: /*
! 2071: If it is a string, what is pointed will be freed
! 2072: later in free_result(). We need to remove the variable to
! 2073: which the user has lost reference.
! 2074: */
! 2075: #ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
! 2076: ZVAL_NULL(stmt->result_bind[param_no].zv);
! 2077: #endif
! 2078: zval_ptr_dtor(&stmt->result_bind[param_no].zv);
! 2079: }
! 2080: }
! 2081:
! 2082: DBG_VOID_RETURN;
! 2083: }
! 2084: /* }}} */
! 2085:
! 2086:
! 2087: /* {{{ mysqlnd_stmt::free_stmt_content */
! 2088: static void
! 2089: MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC)
! 2090: {
! 2091: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2092: DBG_ENTER("mysqlnd_stmt::free_stmt_content");
! 2093: if (!stmt) {
! 2094: DBG_VOID_RETURN;
! 2095: }
! 2096: DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u",
! 2097: stmt->stmt_id, stmt->param_bind, stmt->param_count);
! 2098:
! 2099: /* Destroy the input bind */
! 2100: if (stmt->param_bind) {
! 2101: unsigned int i;
! 2102: /*
! 2103: Because only the bound variables can point to our internal buffers, then
! 2104: separate or free only them. Free is possible because the user could have
! 2105: lost reference.
! 2106: */
! 2107: for (i = 0; i < stmt->param_count; i++) {
! 2108: /*
! 2109: If bind_one_parameter was used, but not everything was
! 2110: bound and nothing was fetched, then some `zv` could be NULL
! 2111: */
! 2112: if (stmt->param_bind[i].zv) {
! 2113: zval_ptr_dtor(&stmt->param_bind[i].zv);
! 2114: }
! 2115: }
! 2116: s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC);
! 2117: stmt->param_bind = NULL;
! 2118: }
! 2119:
! 2120: /*
! 2121: First separate the bound variables, which point to the result set, then
! 2122: destroy the set.
! 2123: */
! 2124: mysqlnd_stmt_separate_result_bind(s TSRMLS_CC);
! 2125: /* Not every statement has a result set attached */
! 2126: if (stmt->result) {
! 2127: stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
! 2128: stmt->result = NULL;
! 2129: }
! 2130:
! 2131: DBG_VOID_RETURN;
! 2132: }
! 2133: /* }}} */
! 2134:
! 2135:
! 2136: /* {{{ mysqlnd_stmt::net_close */
! 2137: static enum_func_status
! 2138: MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
! 2139: {
! 2140: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2141: MYSQLND * conn;
! 2142: zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
! 2143: enum_mysqlnd_collected_stats statistic = STAT_LAST;
! 2144:
! 2145: DBG_ENTER("mysqlnd_stmt::net_close");
! 2146: if (!stmt || !stmt->conn) {
! 2147: DBG_RETURN(FAIL);
! 2148: }
! 2149: DBG_INF_FMT("stmt=%lu", stmt->stmt_id);
! 2150:
! 2151: conn = stmt->conn;
! 2152:
! 2153: SET_EMPTY_ERROR(stmt->error_info);
! 2154: SET_EMPTY_ERROR(stmt->conn->error_info);
! 2155:
! 2156: /*
! 2157: If the user decided to close the statement right after execute()
! 2158: We have to call the appropriate use_result() or store_result() and
! 2159: clean.
! 2160: */
! 2161: do {
! 2162: DBG_INF_FMT("stmt->state=%u", stmt->state);
! 2163: if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) {
! 2164: DBG_INF("fetching result set header");
! 2165: stmt->default_rset_handler(s TSRMLS_CC);
! 2166: stmt->state = MYSQLND_STMT_USER_FETCHING;
! 2167: }
! 2168:
! 2169: /* unbuffered set not fetched to the end ? Clean the line */
! 2170: if (stmt->result) {
! 2171: DBG_INF("skipping result");
! 2172: stmt->result->m.skip_result(stmt->result TSRMLS_CC);
! 2173: }
! 2174: } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS);
! 2175: /*
! 2176: After this point we are allowed to free the result set,
! 2177: as we have cleaned the line
! 2178: */
! 2179: if (stmt->stmt_id) {
! 2180: MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
! 2181: STAT_FREE_RESULT_EXPLICIT);
! 2182:
! 2183: int4store(cmd_buf, stmt->stmt_id);
! 2184: if (CONN_GET_STATE(conn) == CONN_READY &&
! 2185: FAIL == conn->m->simple_command(conn, COM_STMT_CLOSE, (char *)cmd_buf, sizeof(cmd_buf),
! 2186: PROT_LAST /* COM_STMT_CLOSE doesn't send an OK packet*/,
! 2187: FALSE, TRUE TSRMLS_CC)) {
! 2188: stmt->error_info = conn->error_info;
! 2189: DBG_RETURN(FAIL);
! 2190: }
! 2191: }
! 2192: switch (stmt->execute_count) {
! 2193: case 0:
! 2194: statistic = STAT_PS_PREPARED_NEVER_EXECUTED;
! 2195: break;
! 2196: case 1:
! 2197: statistic = STAT_PS_PREPARED_ONCE_USED;
! 2198: break;
! 2199: default:
! 2200: break;
! 2201: }
! 2202: if (statistic != STAT_LAST) {
! 2203: MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
! 2204: }
! 2205:
! 2206: if (stmt->execute_cmd_buffer.buffer) {
! 2207: mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent);
! 2208: stmt->execute_cmd_buffer.buffer = NULL;
! 2209: }
! 2210:
! 2211: s->m->free_stmt_content(s TSRMLS_CC);
! 2212:
! 2213: if (stmt->conn) {
! 2214: stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
! 2215: stmt->conn = NULL;
! 2216: }
! 2217:
! 2218: DBG_RETURN(PASS);
! 2219: }
! 2220: /* }}} */
! 2221:
! 2222: /* {{{ mysqlnd_stmt::dtor */
! 2223: static enum_func_status
! 2224: MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC)
! 2225: {
! 2226: MYSQLND_STMT_DATA * stmt = (s != NULL) ? s->data:NULL;
! 2227: enum_func_status ret = FAIL;
! 2228: zend_bool persistent = (s != NULL) ? s->persistent : 0;
! 2229:
! 2230: DBG_ENTER("mysqlnd_stmt::dtor");
! 2231: if (stmt) {
! 2232:
! 2233: DBG_INF_FMT("stmt=%p", stmt);
! 2234:
! 2235: MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
! 2236: STAT_STMT_CLOSE_EXPLICIT);
! 2237:
! 2238: ret = s->m->net_close(s, implicit TSRMLS_CC);
! 2239: mnd_pefree(stmt, persistent);
! 2240: }
! 2241: mnd_pefree(s, persistent);
! 2242:
! 2243: DBG_INF(ret == PASS? "PASS":"FAIL");
! 2244: DBG_RETURN(ret);
! 2245: }
! 2246: /* }}} */
! 2247:
! 2248:
! 2249: /* {{{ mysqlnd_stmt::alloc_param_bind */
! 2250: static MYSQLND_PARAM_BIND *
! 2251: MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC)
! 2252: {
! 2253: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2254: DBG_ENTER("mysqlnd_stmt::alloc_param_bind");
! 2255: if (!stmt) {
! 2256: DBG_RETURN(NULL);
! 2257: }
! 2258: DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent));
! 2259: }
! 2260: /* }}} */
! 2261:
! 2262:
! 2263: /* {{{ mysqlnd_stmt::alloc_result_bind */
! 2264: static MYSQLND_RESULT_BIND *
! 2265: MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC)
! 2266: {
! 2267: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2268: DBG_ENTER("mysqlnd_stmt::alloc_result_bind");
! 2269: if (!stmt) {
! 2270: DBG_RETURN(NULL);
! 2271: }
! 2272: DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent));
! 2273: }
! 2274: /* }}} */
! 2275:
! 2276:
! 2277: /* {{{ param_bind::free_parameter_bind */
! 2278: PHPAPI void
! 2279: MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC)
! 2280: {
! 2281: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2282: if (stmt) {
! 2283: mnd_pefree(param_bind, stmt->persistent);
! 2284: }
! 2285: }
! 2286: /* }}} */
! 2287:
! 2288:
! 2289: /* {{{ mysqlnd_stmt::free_result_bind */
! 2290: PHPAPI void
! 2291: MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC)
! 2292: {
! 2293: MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
! 2294: if (stmt) {
! 2295: mnd_pefree(result_bind, stmt->persistent);
! 2296: }
! 2297: }
! 2298: /* }}} */
! 2299:
! 2300:
! 2301:
! 2302: MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
! 2303: MYSQLND_METHOD(mysqlnd_stmt, prepare),
! 2304: MYSQLND_METHOD(mysqlnd_stmt, execute),
! 2305: MYSQLND_METHOD(mysqlnd_stmt, use_result),
! 2306: MYSQLND_METHOD(mysqlnd_stmt, store_result),
! 2307: MYSQLND_METHOD(mysqlnd_stmt, get_result),
! 2308: MYSQLND_METHOD(mysqlnd_stmt, more_results),
! 2309: MYSQLND_METHOD(mysqlnd_stmt, next_result),
! 2310: MYSQLND_METHOD(mysqlnd_stmt, free_result),
! 2311: MYSQLND_METHOD(mysqlnd_stmt, data_seek),
! 2312: MYSQLND_METHOD(mysqlnd_stmt, reset),
! 2313: MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
! 2314: MYSQLND_METHOD(mysqlnd_stmt, dtor),
! 2315:
! 2316: MYSQLND_METHOD(mysqlnd_stmt, fetch),
! 2317:
! 2318: MYSQLND_METHOD(mysqlnd_stmt, bind_parameters),
! 2319: MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter),
! 2320: MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param),
! 2321: MYSQLND_METHOD(mysqlnd_stmt, bind_result),
! 2322: MYSQLND_METHOD(mysqlnd_stmt, bind_one_result),
! 2323: MYSQLND_METHOD(mysqlnd_stmt, send_long_data),
! 2324: MYSQLND_METHOD(mysqlnd_stmt, param_metadata),
! 2325: MYSQLND_METHOD(mysqlnd_stmt, result_metadata),
! 2326:
! 2327: MYSQLND_METHOD(mysqlnd_stmt, insert_id),
! 2328: MYSQLND_METHOD(mysqlnd_stmt, affected_rows),
! 2329: MYSQLND_METHOD(mysqlnd_stmt, num_rows),
! 2330:
! 2331: MYSQLND_METHOD(mysqlnd_stmt, param_count),
! 2332: MYSQLND_METHOD(mysqlnd_stmt, field_count),
! 2333: MYSQLND_METHOD(mysqlnd_stmt, warning_count),
! 2334:
! 2335: MYSQLND_METHOD(mysqlnd_stmt, errno),
! 2336: MYSQLND_METHOD(mysqlnd_stmt, error),
! 2337: MYSQLND_METHOD(mysqlnd_stmt, sqlstate),
! 2338:
! 2339: MYSQLND_METHOD(mysqlnd_stmt, attr_get),
! 2340: MYSQLND_METHOD(mysqlnd_stmt, attr_set),
! 2341:
! 2342:
! 2343: MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind),
! 2344: MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind),
! 2345: MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind),
! 2346: MYSQLND_METHOD(mysqlnd_stmt, free_result_bind),
! 2347: MYSQLND_METHOD(mysqlnd_stmt, server_status),
! 2348: mysqlnd_stmt_execute_generate_request,
! 2349: mysqlnd_stmt_execute_parse_response,
! 2350: MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)
! 2351: MYSQLND_CLASS_METHODS_END;
! 2352:
! 2353:
! 2354: /* {{{ _mysqlnd_stmt_init */
! 2355: MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
! 2356: {
! 2357: size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *);
! 2358: MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent);
! 2359: MYSQLND_STMT_DATA * stmt = NULL;
! 2360:
! 2361: DBG_ENTER("_mysqlnd_stmt_init");
! 2362: do {
! 2363: if (!ret) {
! 2364: break;
! 2365: }
! 2366: ret->m = mysqlnd_stmt_methods;
! 2367: ret->persistent = conn->persistent;
! 2368:
! 2369: stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent);
! 2370: DBG_INF_FMT("stmt=%p", stmt);
! 2371: if (!stmt) {
! 2372: break;
! 2373: }
! 2374: stmt->persistent = conn->persistent;
! 2375: stmt->state = MYSQLND_STMT_INITTED;
! 2376: stmt->execute_cmd_buffer.length = 4096;
! 2377: stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent);
! 2378: if (!stmt->execute_cmd_buffer.buffer) {
! 2379: break;
! 2380: }
! 2381:
! 2382: stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
! 2383: /*
! 2384: Mark that we reference the connection, thus it won't be
! 2385: be destructed till there is open statements. The last statement
! 2386: or normal query result will close it then.
! 2387: */
! 2388: stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
! 2389:
! 2390: DBG_RETURN(ret);
! 2391: } while (0);
! 2392:
! 2393: SET_OOM_ERROR(conn->error_info);
! 2394: if (ret) {
! 2395: ret->m->dtor(ret, TRUE TSRMLS_CC);
! 2396: ret = NULL;
! 2397: }
! 2398: DBG_RETURN(NULL);
! 2399: }
! 2400: /* }}} */
! 2401:
! 2402:
! 2403: /* {{{ _mysqlnd_plugin_get_plugin_stmt_data */
! 2404: PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC)
! 2405: {
! 2406: DBG_ENTER("_mysqlnd_plugin_get_plugin_stmt_data");
! 2407: DBG_INF_FMT("plugin_id=%u", plugin_id);
! 2408: if (!stmt || plugin_id >= mysqlnd_plugin_count()) {
! 2409: return NULL;
! 2410: }
! 2411: DBG_RETURN((void *)((char *)stmt + sizeof(MYSQLND_STMT) + plugin_id * sizeof(void *)));
! 2412: }
! 2413: /* }}} */
! 2414:
! 2415:
! 2416: /* {{{ _mysqlnd_init_ps_subsystem */
! 2417: void _mysqlnd_init_ps_subsystem()
! 2418: {
! 2419: mysqlnd_stmt_methods = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_stmt);
! 2420: _mysqlnd_init_ps_fetch_subsystem();
! 2421: }
! 2422: /* }}} */
! 2423:
! 2424:
! 2425: /* {{{ mysqlnd_conn_get_methods */
! 2426: PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods()
! 2427: {
! 2428: return mysqlnd_stmt_methods;
! 2429: }
! 2430: /* }}} */
! 2431:
! 2432:
! 2433: /* {{{ mysqlnd_conn_set_methods */
! 2434: PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods)
! 2435: {
! 2436: mysqlnd_stmt_methods = methods;
! 2437: }
! 2438: /* }}} */
! 2439:
! 2440:
! 2441: /*
! 2442: * Local variables:
! 2443: * tab-width: 4
! 2444: * c-basic-offset: 4
! 2445: * End:
! 2446: * vim600: noet sw=4 ts=4 fdm=marker
! 2447: * vim<600: noet sw=4 ts=4
! 2448: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>