diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_ps.c | 929 |
1 files changed, 595 insertions, 334 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 06ec510f5..fa8be3ec2 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -1,8 +1,8 @@ /* +----------------------------------------------------------------------+ - | PHP Version 6 | + | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2009 The PHP Group | + | Copyright (c) 2006-2010 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps.c 294543 2010-02-04 20:28:55Z johannes $ */ +/* $Id: mysqlnd_ps.c 300735 2010-06-24 19:52:13Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -39,11 +39,7 @@ const char * const mysqlnd_stmt_not_prepared = "Statement not prepared"; static struct st_mysqlnd_stmt_methods *mysqlnd_stmt_methods; /* Exported by mysqlnd_ps_codec.c */ -zend_uchar* mysqlnd_stmt_execute_generate_request(MYSQLND_STMT *stmt, size_t *request_len, - zend_bool *free_buffer TSRMLS_DC); - - -MYSQLND_RES * _mysqlnd_stmt_use_result(MYSQLND_STMT *stmt TSRMLS_DC); +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); enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, @@ -57,20 +53,25 @@ static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_D static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); static void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC); -static enum_func_status mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC); /* {{{ mysqlnd_stmt::store_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; - MYSQLND_RES *result; + MYSQLND * conn; + MYSQLND_RES * result; zend_bool to_cache = FALSE; DBG_ENTER("mysqlnd_stmt::store_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { DBG_RETURN(NULL); @@ -78,7 +79,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); + DBG_RETURN(s->m->use_result(s TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -90,7 +91,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) DBG_RETURN(NULL); } - stmt->default_rset_handler = stmt->m->store_result; + stmt->default_rset_handler = s->m->store_result; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -101,7 +102,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered; result->m.fetch_lengths = NULL;/* makes no sense */ - result->result_set_memory_pool = mysqlnd_mempool_create(16000 TSRMLS_CC); + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE, to_cache TSRMLS_CC); @@ -125,14 +126,20 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::get_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC) { - MYSQLND *conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; MYSQLND_RES *result; DBG_ENTER("mysqlnd_stmt::get_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + /* be compliant with libmysql - NULL will turn */ if (!stmt->field_count) { DBG_RETURN(NULL); @@ -140,7 +147,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); + DBG_RETURN(s->m->use_result(s TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -154,31 +161,47 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) SET_EMPTY_ERROR(stmt->conn->error_info); MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = mysqlnd_result_init(stmt->result->field_count TSRMLS_CC); + do { + result = conn->m->result_init(stmt->result->field_count, stmt->persistent TSRMLS_CC); + if (!result) { + SET_OOM_ERROR(stmt->conn->error_info); + break; + } - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + if (!result->meta) { + SET_OOM_ERROR(stmt->conn->error_info); + break; + } - if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { - stmt->upsert_status.affected_rows = result->stored_data->row_count; - stmt->state = MYSQLND_STMT_PREPARED; - result->type = MYSQLND_RES_PS_BUF; - } else { - stmt->error_info = conn->error_info; - stmt->state = MYSQLND_STMT_PREPARED; - } + if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { + stmt->upsert_status.affected_rows = result->stored_data->row_count; + stmt->state = MYSQLND_STMT_PREPARED; + result->type = MYSQLND_RES_PS_BUF; + } else { + stmt->error_info = conn->error_info; + stmt->state = MYSQLND_STMT_PREPARED; + break; + } + DBG_RETURN(result); + } while (0); - DBG_RETURN(result); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + DBG_RETURN(NULL); } /* }}} */ /* {{{ mysqlnd_stmt::more_results */ static zend_bool -MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::more_results"); /* (conn->state == CONN_NEXT_RESULT_PENDING) too */ - DBG_RETURN((stmt->conn && (stmt->conn->upsert_status.server_status & + DBG_RETURN((stmt && stmt->conn && (stmt->conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS))? TRUE: FALSE); @@ -188,50 +211,61 @@ MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::next_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC) { - MYSQLND *conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt::next_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); - if (!conn || - CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || - !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS) || - !stmt->result) - { + if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING || !(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS)) { DBG_RETURN(FAIL); } /* Free space for next result */ - mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + mysqlnd_internal_free_stmt_content(s TSRMLS_CC); - DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC)); + DBG_RETURN(s->m->parse_execute_response(s TSRMLS_CC)); } /* }}} */ /* {{{ mysqlnd_stmt_skip_metadata */ static enum_func_status -mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_skip_metadata(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; /* Follows parameter metadata, we have just to skip it, as libmysql does */ unsigned int i = 0; - enum_func_status ret = PASS; + enum_func_status ret = FAIL; MYSQLND_PACKET_RES_FIELD * field_packet; DBG_ENTER("mysqlnd_stmt_skip_metadata"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); field_packet = stmt->conn->protocol->m.get_result_field_packet(stmt->conn->protocol, FALSE TSRMLS_CC); - field_packet->skip_parsing = TRUE; - for (;i < stmt->param_count; i++) { - if (FAIL == PACKET_READ(field_packet, stmt->conn)) { - ret = FAIL; - break; + if (!field_packet) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); + } else { + ret = PASS; + field_packet->skip_parsing = TRUE; + for (;i < stmt->param_count; i++) { + if (FAIL == PACKET_READ(field_packet, stmt->conn)) { + ret = FAIL; + break; + } } + PACKET_FREE(field_packet); } - PACKET_FREE(field_packet); DBG_RETURN(ret); } @@ -240,26 +274,34 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_read_prepare_response */ static enum_func_status -mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_read_prepare_response(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_PACKET_PREPARE_RESPONSE * prepare_resp; - enum_func_status ret = PASS; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_stmt_read_prepare_response"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); prepare_resp = stmt->conn->protocol->m.get_prepare_response_packet(stmt->conn->protocol, FALSE TSRMLS_CC); + if (!prepare_resp) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); + goto done; + } + if (FAIL == PACKET_READ(prepare_resp, stmt->conn)) { - ret = FAIL; goto done; } if (0xFF == prepare_resp->error_code) { stmt->error_info = stmt->conn->error_info = prepare_resp->error_info; - ret = FAIL; goto done; } - + ret = PASS; stmt->stmt_id = prepare_resp->stmt_id; stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp->warning_count; stmt->field_count = stmt->conn->field_count = prepare_resp->field_count; @@ -274,28 +316,37 @@ done: /* {{{ mysqlnd_stmt_prepare_read_eof */ static enum_func_status -mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) +mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_PACKET_EOF * fields_eof; - enum_func_status ret; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_stmt_prepare_read_eof"); + if (!stmt || !stmt->conn || !stmt->conn->protocol) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); fields_eof = stmt->conn->protocol->m.get_eof_packet(stmt->conn->protocol, FALSE TSRMLS_CC); - if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) { - if (stmt->result) { - stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); - mnd_efree(stmt->result); - memset(stmt, 0, sizeof(MYSQLND_STMT)); - stmt->state = MYSQLND_STMT_INITTED; - } + if (!fields_eof) { + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(stmt->conn->error_info); } else { - stmt->upsert_status.server_status = fields_eof->server_status; - stmt->upsert_status.warning_count = fields_eof->warning_count; - stmt->state = MYSQLND_STMT_PREPARED; + if (FAIL == (ret = PACKET_READ(fields_eof, stmt->conn))) { + if (stmt->result) { + stmt->result->m.free_result_contents(stmt->result TSRMLS_CC); + mnd_efree(stmt->result); + memset(stmt, 0, sizeof(MYSQLND_STMT_DATA)); + stmt->state = MYSQLND_STMT_INITTED; + } + } else { + stmt->upsert_status.server_status = fields_eof->server_status; + stmt->upsert_status.warning_count = fields_eof->warning_count; + stmt->state = MYSQLND_STMT_PREPARED; + } + PACKET_FREE(fields_eof); } - PACKET_FREE(fields_eof); DBG_RETURN(ret); } @@ -304,11 +355,16 @@ mysqlnd_stmt_prepare_read_eof(MYSQLND_STMT *stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::prepare */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * const query, unsigned int query_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const s, const char * const query, unsigned int query_len TSRMLS_DC) { - MYSQLND_STMT *stmt_to_prepare = stmt; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND_STMT * s_to_prepare = s; + MYSQLND_STMT_DATA * stmt_to_prepare = stmt; DBG_ENTER("mysqlnd_stmt::prepare"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_ERROR_AFF_ROWS(stmt); @@ -321,29 +377,33 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co /* See if we have to clean the wire */ if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } /* No 'else' here please :) */ - if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { + if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE && stmt->result) { stmt->result->m.skip_result(stmt->result TSRMLS_CC); } /* Create a new test statement, which we will prepare, but if anything fails, we will scrap it. */ - stmt_to_prepare = mysqlnd_stmt_init(stmt->conn); + s_to_prepare = stmt->conn->m->stmt_init(stmt->conn TSRMLS_CC); + if (!s_to_prepare) { + goto fail; + } + stmt_to_prepare = s_to_prepare->data; } - 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) || - FAIL == mysqlnd_stmt_read_prepare_response(stmt_to_prepare TSRMLS_CC)) { + 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) || + FAIL == mysqlnd_stmt_read_prepare_response(s_to_prepare TSRMLS_CC)) + { goto fail; } if (stmt_to_prepare->param_count) { - if (FAIL == mysqlnd_stmt_skip_metadata(stmt_to_prepare TSRMLS_CC) || - FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) + if (FAIL == mysqlnd_stmt_skip_metadata(s_to_prepare TSRMLS_CC) || + FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC)) { goto fail; } @@ -355,7 +415,11 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co no metadata at prepare. */ if (stmt_to_prepare->field_count) { - MYSQLND_RES *result = mysqlnd_result_init(stmt_to_prepare->field_count TSRMLS_CC); + MYSQLND_RES * result = stmt->conn->m->result_init(stmt_to_prepare->field_count, stmt_to_prepare->persistent TSRMLS_CC); + if (!result) { + SET_OOM_ERROR(stmt->conn->error_info); + goto fail; + } /* Allocate the result now as it is needed for the reading of metadata */ stmt_to_prepare->result = result; @@ -364,27 +428,28 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co result->type = MYSQLND_RES_PS_BUF; if (FAIL == result->m.read_result_metadata(result, stmt_to_prepare->conn TSRMLS_CC) || - FAIL == mysqlnd_stmt_prepare_read_eof(stmt_to_prepare TSRMLS_CC)) { + FAIL == mysqlnd_stmt_prepare_read_eof(s_to_prepare TSRMLS_CC)) + { goto fail; } } if (stmt_to_prepare != stmt) { /* Free old buffers, binding and resources on server */ - stmt->m->net_close(stmt, TRUE TSRMLS_CC); + s->m->net_close(s, TRUE TSRMLS_CC); - memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT)); + memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT_DATA)); /* Now we will have a clean new statement object */ - mnd_efree(stmt_to_prepare); + mnd_pefree(stmt_to_prepare, stmt_to_prepare->persistent); } stmt->state = MYSQLND_STMT_PREPARED; DBG_INF("PASS"); DBG_RETURN(PASS); fail: - if (stmt_to_prepare != stmt) { - stmt_to_prepare->m->dtor(stmt_to_prepare, TRUE TSRMLS_CC); + if (stmt_to_prepare != stmt && s_to_prepare) { + s_to_prepare->m->dtor(s_to_prepare, TRUE TSRMLS_CC); } stmt->state = MYSQLND_STMT_INITTED; @@ -396,16 +461,20 @@ fail: /* {{{ mysqlnd_stmt_execute_parse_response */ static enum_func_status -mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) +mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt_execute_parse_response"); - + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; CONN_SET_STATE(conn, CONN_QUERY_SENT); - ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC); + ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC); if (ret == FAIL) { stmt->error_info = conn->error_info; stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows; @@ -443,7 +512,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) use_result() or store_result() and we should be able to scrap the data on the line, if he just decides to close the statement. */ - DBG_INF_FMT("server_status=%d cursor=%d", stmt->upsert_status.server_status, + DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status.server_status, stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS); if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) { @@ -451,7 +520,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) stmt->cursor_exists = TRUE; CONN_SET_STATE(conn, CONN_READY); /* Only cursor read */ - stmt->default_rset_handler = stmt->m->use_result; + stmt->default_rset_handler = s->m->use_result; DBG_INF("use_result"); } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) { DBG_INF("asked for cursor but got none"); @@ -466,12 +535,12 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) precached on client and server's resources are freed. */ /* preferred is buffered read */ - stmt->default_rset_handler = stmt->m->store_result; + stmt->default_rset_handler = s->m->store_result; DBG_INF("store_result"); } else { DBG_INF("no cursor"); /* preferred is unbuffered read */ - stmt->default_rset_handler = stmt->m->use_result; + stmt->default_rset_handler = s->m->use_result; DBG_INF("use_result"); } } @@ -485,15 +554,20 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::execute */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; - MYSQLND *conn = stmt->conn; - zend_uchar *request; + MYSQLND * conn; + zend_uchar *request = NULL; size_t request_len; zend_bool free_request; DBG_ENTER("mysqlnd_stmt::execute"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + conn = stmt->conn; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_ERROR_AFF_ROWS(stmt); @@ -538,8 +612,8 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { @@ -583,23 +657,25 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } if (not_bound) { char * msg; - spprintf(&msg, 0, "No data supplied for %d parameter%s in prepared statement", + spprintf(&msg, 0, "No data supplied for %u parameter%s in prepared statement", not_bound, not_bound>1 ?"s":""); SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, msg); if (msg) { - efree(msg); + efree(msg); /* allocated by spprintf */ } DBG_INF("FAIL"); DBG_RETURN(FAIL); } } - request = mysqlnd_stmt_execute_generate_request(stmt, &request_len, &free_request TSRMLS_CC); - - /* support for buffer types should be added here ! */ - - ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, - PROT_LAST /* we will handle the response packet*/, - FALSE, FALSE TSRMLS_CC); + ret = s->m->generate_execute_request(s, &request, &request_len, &free_request TSRMLS_CC); + if (ret == PASS) { + /* support for buffer types should be added here ! */ + ret = stmt->conn->m->simple_command(stmt->conn, COM_STMT_EXECUTE, (char *)request, request_len, + PROT_LAST /* we will handle the response packet*/, + FALSE, FALSE TSRMLS_CC); + } else { + SET_STMT_ERROR(stmt, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Couldn't generate the request. Possibly OOM."); + } if (free_request) { mnd_efree(request); @@ -612,7 +688,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) } stmt->execute_count++; - ret = mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC); + ret = s->m->parse_execute_response(s TSRMLS_CC); if (ret == PASS && conn->last_query_type == QUERY_UPSERT && stmt->upsert_status.affected_rows) { MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_PS, stmt->upsert_status.affected_rows); @@ -626,11 +702,13 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES_BUFFERED *set = result->stored_data; unsigned int field_count = result->meta->field_count; DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); + *fetched_anything = FALSE; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); /* If we haven't read everything */ @@ -645,15 +723,18 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + meta->field_count, + meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - meta->field_count, - meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); if (stmt->update_max_length) { for (i = 0; i < result->field_count; i++) { /* @@ -680,7 +761,7 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("i=%d type=%d", i, Z_TYPE_P(current_row[i])); + DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i])); if (Z_TYPE_P(current_row[i]) != IS_NULL) { /* Copy the value. @@ -708,7 +789,6 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f DBG_INF("row fetched"); } else { set->data_cursor = NULL; - *fetched_anything = FALSE; DBG_INF("no more data"); } DBG_INF("PASS"); @@ -722,14 +802,16 @@ static enum_func_status mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND_PACKET_ROW * row_packet; DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); + *fetched_anything = FALSE; + if (result->unbuf->eof_reached) { /* No more rows obviously */ - *fetched_anything = FALSE; DBG_INF("eof reached"); DBG_RETURN(PASS); } @@ -739,6 +821,10 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } + if (!(row_packet = result->row_packet)) { + DBG_RETURN(FAIL); + } + /* Let the row packet fill our buffer and skip additional malloc + memcpy */ row_packet->skip_extraction = stmt && stmt->result_bind? FALSE:TRUE; @@ -748,8 +834,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; if (!row_packet->skip_extraction) { result->m.unbuffered_free_last_data(result TSRMLS_CC); @@ -760,14 +844,17 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { @@ -805,11 +892,13 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else if (ret == FAIL) { - *fetched_anything = FALSE; if (row_packet->error_info.error_no) { stmt->conn->error_info = row_packet->error_info; stmt->error_info = row_packet->error_info; @@ -817,7 +906,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { - *fetched_anything = FALSE; DBG_INF("EOF"); /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; @@ -834,7 +922,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int } } - DBG_INF_FMT("ret=%s fetched_anything=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); DBG_RETURN(ret); } /* }}} */ @@ -842,14 +930,20 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int /* {{{ mysqlnd_stmt::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT *stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES *result; - MYSQLND *conn = stmt->conn; + MYSQLND * conn; DBG_ENTER("mysqlnd_stmt::use_result"); + if (!stmt || !stmt->conn || !stmt->result) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + if (!stmt->field_count || (!stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_FETCHING_DATA) || (stmt->cursor_exists && CONN_GET_STATE(conn) != CONN_READY) || @@ -885,13 +979,14 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { enum_func_status ret; - MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; + MYSQLND_STMT * s = (MYSQLND_STMT *) param; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; zend_uchar buf[STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */]; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; + MYSQLND_PACKET_ROW * row_packet; DBG_ENTER("mysqlnd_fetch_stmt_row_cursor"); - if (!stmt) { + if (!stmt || !stmt->conn || !result || !result->conn || !result->unbuf) { DBG_ERR("no statement"); DBG_RETURN(FAIL); } @@ -905,6 +1000,9 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } + if (!(row_packet = result->row_packet)) { + DBG_RETURN(FAIL); + } SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -923,10 +1021,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; - DBG_INF_FMT("skip_extraction=%d", row_packet->skip_extraction); + DBG_INF_FMT("skip_extraction=%u", row_packet->skip_extraction); if (!row_packet->skip_extraction) { result->m.unbuffered_free_last_data(result TSRMLS_CC); @@ -936,14 +1032,17 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } /* If no result bind, do nothing. We consumed the data */ for (i = 0; i < field_count; i++) { @@ -956,7 +1055,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF zval_dtor(stmt->result_bind[i].zv); #endif - DBG_INF_FMT("i=%d bound_var=%p type=%d refc=%u", i, stmt->result_bind[i].zv, + DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv, Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv)); if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) { if ((Z_TYPE_P(data) == IS_STRING @@ -982,16 +1081,19 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla the bound variables. Thus we need to do part of what it does or Zend will report leaks. */ - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } /* We asked for one row, the next one should be EOF, eat it */ ret = PACKET_READ(row_packet, result->conn); if (row_packet->row_buffer) { - row_packet->row_buffer->free_chunk(row_packet->row_buffer, TRUE TSRMLS_CC); + row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR); + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else { *fetched_anything = FALSE; @@ -1012,7 +1114,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla stmt->conn->upsert_status.server_status = row_packet->server_status; - DBG_INF_FMT("ret=%s fetched=%d server_status=%d warnings=%d eof=%d", + DBG_INF_FMT("ret=%s fetched=%u server_status=%u warnings=%u eof=%u", ret == PASS? "PASS":"FAIL", *fetched_anything, row_packet->server_status, row_packet->warning_count, result->unbuf->eof_reached); @@ -1023,10 +1125,14 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla /* {{{ mysqlnd_stmt::fetch */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fetched_anything TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; DBG_ENTER("mysqlnd_stmt::fetch"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); if (!stmt->result || @@ -1038,14 +1144,14 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const } else if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { /* Execute only once. We have to free the previous contents of user's bound vars */ - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler(s TSRMLS_CC); } stmt->state = MYSQLND_STMT_USER_FETCHING; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); - DBG_INF_FMT("result_bind=%p separated_once=%d", stmt->result_bind, stmt->result_zvals_separated_once); + DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once); /* The user might have not bound any variables for result. Do the binding once she does it. @@ -1065,7 +1171,7 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const stmt->result_zvals_separated_once = TRUE; } - ret = stmt->result->m.fetch_row(stmt->result, (void*)stmt, 0, fetched_anything TSRMLS_CC); + ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything TSRMLS_CC); DBG_RETURN(ret); } /* }}} */ @@ -1073,12 +1179,16 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const /* {{{ mysqlnd_stmt::reset */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret = PASS; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; DBG_ENTER("mysqlnd_stmt::reset"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); SET_EMPTY_ERROR(stmt->error_info); @@ -1102,16 +1212,18 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) We have to call the appropriate use_result() or store_result() and clean. */ - if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { - DBG_INF("fetching result set header"); - stmt->default_rset_handler(stmt TSRMLS_CC); - stmt->state = MYSQLND_STMT_USER_FETCHING; - } + do { + if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { + DBG_INF("fetching result set header"); + stmt->default_rset_handler(s TSRMLS_CC); + stmt->state = MYSQLND_STMT_USER_FETCHING; + } - if (stmt->result) { - DBG_INF("skipping result"); - stmt->result->m.skip_result(stmt->result TSRMLS_CC); - } + if (stmt->result) { + DBG_INF("skipping result"); + stmt->result->m.skip_result(stmt->result TSRMLS_CC); + } + } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS); /* Don't free now, let the result be usable. When the stmt will again be @@ -1138,16 +1250,22 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::send_long_data */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned int param_no, +MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no, const char * const data, unsigned long length TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret = FAIL; - MYSQLND * conn = stmt->conn; + MYSQLND * conn; zend_uchar *cmd_buf; enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; DBG_ENTER("mysqlnd_stmt::send_long_data"); - DBG_INF_FMT("stmt=%lu param_no=%d data_len=%lu", stmt->stmt_id, param_no, length); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + DBG_INF_FMT("stmt=%lu param_no=%u data_len=%lu", stmt->stmt_id, param_no, length); + + conn = stmt->conn; SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -1186,19 +1304,25 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned if (CONN_GET_STATE(conn) == CONN_READY) { size_t packet_len; - stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; cmd_buf = mnd_emalloc(packet_len = STMT_ID_LENGTH + 2 + length); - - int4store(cmd_buf, stmt->stmt_id); - int2store(cmd_buf + STMT_ID_LENGTH, param_no); - memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); - - /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ - ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, - PROT_LAST , FALSE, TRUE TSRMLS_CC); - mnd_efree(cmd_buf); - if (FAIL == ret) { - stmt->error_info = conn->error_info; + if (cmd_buf) { + stmt->param_bind[param_no].flags |= MYSQLND_PARAM_BIND_BLOB_USED; + + int4store(cmd_buf, stmt->stmt_id); + int2store(cmd_buf + STMT_ID_LENGTH, param_no); + memcpy(cmd_buf + STMT_ID_LENGTH + 2, data, length); + + /* COM_STMT_SEND_LONG_DATA doesn't send an OK packet*/ + ret = conn->m->simple_command(conn, cmd, (char *)cmd_buf, packet_len, + PROT_LAST , FALSE, TRUE TSRMLS_CC); + mnd_efree(cmd_buf); + if (FAIL == ret) { + stmt->error_info = conn->error_info; + } + } else { + ret = FAIL; + SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(conn->error_info); } /* Cover protocol error: COM_STMT_SEND_LONG_DATA was designed to be quick and not @@ -1240,16 +1364,20 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned /* {{{ mysqlnd_stmt::bind_parameters */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_param"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); DBG_ERR("not prepared"); - if (param_bind && stmt->param_bind_dtor) { - stmt->param_bind_dtor(param_bind TSRMLS_CC); + if (param_bind) { + s->m->free_parameter_bind(s, param_bind TSRMLS_CC); } DBG_RETURN(FAIL); } @@ -1261,8 +1389,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND unsigned int i = 0; if (!param_bind) { - SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, - "Re-binding (still) not supported"); + SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported"); DBG_ERR("Re-binding (still) not supported"); DBG_RETURN(FAIL); } else if (stmt->param_bind) { @@ -1281,15 +1408,15 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND zval_ptr_dtor(&stmt->param_bind[i].zv); } } - if (stmt->param_bind != param_bind && stmt->param_bind_dtor) { - stmt->param_bind_dtor(stmt->param_bind TSRMLS_CC); + if (stmt->param_bind != param_bind) { + s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); } } stmt->param_bind = param_bind; for (i = 0; i < stmt->param_count; i++) { /* The client will use stmt_send_long_data */ - DBG_INF_FMT("%d is of type %d", i, stmt->param_bind[i].type); + DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type); /* Prevent from freeing */ /* Don't update is_ref, or we will leak during conversion */ Z_ADDREF_P(stmt->param_bind[i].zv); @@ -1308,11 +1435,15 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, MYSQLND /* {{{ mysqlnd_stmt::bind_one_parameter */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, +MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigned int param_no, zval * const zv, zend_uchar type TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_one_parameter"); - DBG_INF_FMT("stmt=%lu param_no=%d param_count=%u type=%d", + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } + DBG_INF_FMT("stmt=%lu param_no=%u param_count=%u type=%u", stmt->stmt_id, param_no, stmt->param_count, type); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1332,6 +1463,9 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsi if (stmt->param_count) { if (!stmt->param_bind) { stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND)); + if (!stmt->param_bind) { + DBG_RETURN(FAIL); + } } /* Prevent from freeing */ @@ -1359,9 +1493,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsi /* {{{ mysqlnd_stmt::refresh_bind_param */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::refresh_bind_param"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu param_count=%u", stmt->stmt_id, stmt->param_count); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1382,32 +1520,22 @@ MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRML /* }}} */ -/* {{{ mysqlnd_stmt::set_bind_param_dtor */ -static void -MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor)(MYSQLND_STMT * const stmt, - void (*param_bind_dtor)(MYSQLND_PARAM_BIND * dtor TSRMLS_DC) TSRMLS_DC) -{ - DBG_ENTER("mysqlnd_stmt::set_bind_param_dtor"); - DBG_INF_FMT("stmt=%p", param_bind_dtor); - stmt->param_bind_dtor = param_bind_dtor; - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * const result_bind TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); - if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); - if (result_bind && stmt->result_bind_dtor) { - stmt->result_bind_dtor(result_bind TSRMLS_CC); + if (result_bind) { + s->m->free_result_bind(s, result_bind TSRMLS_CC); } DBG_ERR("not prepared"); DBG_RETURN(FAIL); @@ -1424,7 +1552,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, DBG_RETURN(FAIL); } - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); stmt->result_zvals_separated_once = FALSE; stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { @@ -1438,8 +1566,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, */ stmt->result_bind[i].bound = TRUE; } - } else if (result_bind && stmt->result_bind_dtor) { - stmt->result_bind_dtor(result_bind TSRMLS_CC); + } else if (result_bind) { + s->m->free_result_bind(s, result_bind TSRMLS_CC); } DBG_INF("PASS"); DBG_RETURN(PASS); @@ -1449,9 +1577,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::bind_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::bind_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); if (stmt->state < MYSQLND_STMT_PREPARED) { @@ -1470,19 +1602,22 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigne SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->field_count) { - mysqlnd_stmt_separate_one_result_bind(stmt, param_no TSRMLS_CC); + mysqlnd_stmt_separate_one_result_bind(s, param_no TSRMLS_CC); /* Guaranteed is that stmt->result_bind is NULL */ if (!stmt->result_bind) { stmt->result_bind = mnd_ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND)); } else { stmt->result_bind = mnd_erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND)); } + if (!stmt->result_bind) { + DBG_RETURN(FAIL); + } ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv); /* Don't update is_ref !!! it's not our job Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt will fail. - */ + */ stmt->result_bind[param_no].bound = TRUE; } DBG_INF("PASS"); @@ -1491,117 +1626,124 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const stmt, unsigne /* }}} */ -/* {{{ mysqlnd_stmt::set_bind_result_dtor */ -static void -MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor)(MYSQLND_STMT * const stmt, - void (*result_bind_dtor)(MYSQLND_RESULT_BIND * dtor TSRMLS_DC) TSRMLS_DC) -{ - DBG_ENTER("mysqlnd_stmt::set_bind_param_dtor"); - DBG_INF_FMT("stmt=%p", result_bind_dtor); - stmt->result_bind_dtor = result_bind_dtor; - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_stmt::insert_id */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.last_insert_id; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.last_insert_id : 0; } /* }}} */ /* {{{ mysqlnd_stmt::affected_rows */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, affected_rows)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.affected_rows; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.affected_rows : 0; } /* }}} */ /* {{{ mysqlnd_stmt::num_rows */ static uint64_t -MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, num_rows)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->result? mysqlnd_num_rows(stmt->result):0; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->result? mysqlnd_num_rows(stmt->result):0; } /* }}} */ /* {{{ mysqlnd_stmt::warning_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, warning_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->upsert_status.warning_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.warning_count : 0; +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::server_status */ +static unsigned int +MYSQLND_METHOD(mysqlnd_stmt, server_status)(const MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->upsert_status.server_status : 0; } /* }}} */ /* {{{ mysqlnd_stmt::field_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, field_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->field_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->field_count : 0; } /* }}} */ /* {{{ mysqlnd_stmt::param_count */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, param_count)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->param_count; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->param_count : 0; } /* }}} */ /* {{{ mysqlnd_stmt::errno */ static unsigned int -MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, errno)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.error_no; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->error_info.error_no : 0; } /* }}} */ /* {{{ mysqlnd_stmt::error */ static const char * -MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, error)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.error; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt? stmt->error_info.error : 0; } /* }}} */ /* {{{ mysqlnd_stmt::sqlstate */ static const char * -MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, sqlstate)(const MYSQLND_STMT * const s TSRMLS_DC) { - return stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->error_info.sqlstate[0] ? stmt->error_info.sqlstate:MYSQLND_SQLSTATE_NULL; } /* }}} */ /* {{{ mysqlnd_stmt::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const stmt, uint64_t row TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, data_seek)(const MYSQLND_STMT * const s, uint64_t row TSRMLS_DC) { - return stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + return stmt && stmt->result? stmt->result->m.seek_data(stmt->result, row TSRMLS_CC) : FAIL; } /* }}} */ /* {{{ mysqlnd_stmt::param_metadata */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const s TSRMLS_DC) { - if (!stmt->param_count) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (!stmt || !stmt->param_count) { return NULL; } - return NULL; } /* }}} */ @@ -1609,11 +1751,15 @@ MYSQLND_METHOD(mysqlnd_stmt, param_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC /* {{{ mysqlnd_stmt::result_metadata */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES *result; DBG_ENTER("mysqlnd_stmt::result_metadata"); + if (!stmt) { + DBG_RETURN(NULL); + } DBG_INF_FMT("stmt=%u field_count=%u", stmt->stmt_id, stmt->field_count); if (!stmt->field_count || !stmt->conn || !stmt->result || !stmt->result->meta) { @@ -1634,27 +1780,48 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const stmt TSRMLS_D In the meantime we don't need a zval cache reference for this fake result set, so we don't get one. */ - result = mysqlnd_result_init(stmt->field_count TSRMLS_CC); - result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); - result->unbuf->eof_reached = TRUE; - result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); - - DBG_INF_FMT("result=%p", result); - DBG_RETURN(result); + do { + result = stmt->conn->m->result_init(stmt->field_count, stmt->persistent TSRMLS_CC); + if (!result) { + break; + } + result->type = MYSQLND_RES_NORMAL; + result->m.fetch_row = result->m.fetch_row_normal_unbuffered; + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->unbuf) { + break; + } + result->unbuf->eof_reached = TRUE; + result->meta = stmt->result->meta->m->clone_metadata(stmt->result->meta, FALSE TSRMLS_CC); + if (!result->meta) { + break; + } + + DBG_INF_FMT("result=%p", result); + DBG_RETURN(result); + } while (0); + + SET_OOM_ERROR(stmt->conn->error_info); + if (result) { + result->m.free_result(result, TRUE TSRMLS_CC); + } + DBG_RETURN(NULL); } /* }}} */ /* {{{ mysqlnd_stmt::attr_set */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s, enum mysqlnd_stmt_attr attr_type, const void * const value TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; unsigned long val = *(unsigned long *) value; DBG_ENTER("mysqlnd_stmt::attr_set"); + if (!stmt) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu attr_type=%u value=%lu", stmt->stmt_id, attr_type, val); switch (attr_type) { @@ -1697,11 +1864,15 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, /* {{{ mysqlnd_stmt::attr_get */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt, +MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s, enum mysqlnd_stmt_attr attr_type, void * const value TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::attr_set"); + if (!stmt) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu attr_type=%u", stmt->stmt_id, attr_type); switch (attr_type) { @@ -1725,9 +1896,13 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt, /* free_result() doesn't actually free stmt->result but only the buffers */ /* {{{ mysqlnd_stmt::free_result */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt::free_result"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); if (!stmt->result) { @@ -1742,8 +1917,8 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); /* Do implicit use_result and then flush the result */ - stmt->default_rset_handler = stmt->m->use_result; - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler = s->m->use_result; + stmt->default_rset_handler(s TSRMLS_CC); } if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) { @@ -1754,7 +1929,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) Separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); /* Now we can destroy the result set */ stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC); @@ -1774,11 +1949,16 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_separate_result_bind */ -void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) +static void +mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; unsigned int i; DBG_ENTER("mysqlnd_stmt_separate_result_bind"); + if (!stmt) { + DBG_VOID_RETURN; + } DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count); @@ -1794,7 +1974,7 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("%d has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv)); + DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv)); /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free @@ -1817,9 +1997,7 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) } } } - if (stmt->result_bind_dtor) { - stmt->result_bind_dtor(stmt->result_bind TSRMLS_CC); - } + s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC); stmt->result_bind = NULL; DBG_VOID_RETURN; @@ -1828,10 +2006,15 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt_separate_one_result_bind */ -void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC) +static void +mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param_no TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_stmt_separate_one_result_bind"); - DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%d", + if (!stmt) { + DBG_VOID_RETURN; + } + DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u", stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no); if (!stmt->result_bind) { @@ -1845,7 +2028,7 @@ void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned i */ /* Let's try with no cache */ if (stmt->result_bind[param_no].bound == TRUE) { - DBG_INF_FMT("%d has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); + DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); /* We have to separate the actual zval value of the bound variable from our allocated zvals or we will face double-free @@ -1874,10 +2057,14 @@ void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned i /* {{{ mysqlnd_internal_free_stmt_content */ -static -void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) +static void +mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const s TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; DBG_ENTER("mysqlnd_internal_free_stmt_content"); + if (!stmt) { + DBG_VOID_RETURN; + } DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u", stmt->stmt_id, stmt->param_bind, stmt->param_count); @@ -1898,9 +2085,7 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) zval_ptr_dtor(&stmt->param_bind[i].zv); } } - if (stmt->param_bind_dtor) { - stmt->param_bind_dtor(stmt->param_bind TSRMLS_CC); - } + s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); stmt->param_bind = NULL; } @@ -1908,7 +2093,7 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) First separate the bound variables, which point to the result set, then destroy the set. */ - mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC); + mysqlnd_stmt_separate_result_bind(s TSRMLS_CC); /* Not every statement has a result set attached */ if (stmt->result) { stmt->result->m.free_result_internal(stmt->result TSRMLS_CC); @@ -1922,15 +2107,21 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC) /* {{{ mysqlnd_stmt::net_close */ static enum_func_status -MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC) { - MYSQLND * conn = stmt->conn; + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + MYSQLND * conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; enum_mysqlnd_collected_stats stat = STAT_LAST; DBG_ENTER("mysqlnd_stmt::net_close"); + if (!stmt || !stmt->conn) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("stmt=%lu", stmt->stmt_id); + conn = stmt->conn; + SET_EMPTY_ERROR(stmt->error_info); SET_EMPTY_ERROR(stmt->conn->error_info); @@ -1940,10 +2131,10 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ clean. */ do { - DBG_INF_FMT("stmt->state=%d", stmt->state); + DBG_INF_FMT("stmt->state=%u", stmt->state); if (stmt->state == MYSQLND_STMT_WAITING_USE_OR_STORE) { DBG_INF("fetching result set header"); - stmt->default_rset_handler(stmt TSRMLS_CC); + stmt->default_rset_handler(s TSRMLS_CC); stmt->state = MYSQLND_STMT_USER_FETCHING; } @@ -1952,7 +2143,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ DBG_INF("skipping result"); stmt->result->m.skip_result(stmt->result TSRMLS_CC); } - } while (mysqlnd_stmt_more_results(stmt) && mysqlnd_stmt_next_result(stmt) == PASS); + } while (mysqlnd_stmt_more_results(s) && mysqlnd_stmt_next_result(s) == PASS); /* After this point we are allowed to free the result set, as we have cleaned the line @@ -1985,11 +2176,11 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ } if (stmt->execute_cmd_buffer.buffer) { - mnd_efree(stmt->execute_cmd_buffer.buffer); + mnd_pefree(stmt->execute_cmd_buffer.buffer, stmt->persistent); stmt->execute_cmd_buffer.buffer = NULL; } - mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC); + mysqlnd_internal_free_stmt_content(s TSRMLS_CC); if (stmt->conn) { stmt->conn->m->free_reference(stmt->conn TSRMLS_CC); @@ -2002,18 +2193,24 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_ /* {{{ mysqlnd_stmt::dtor */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const s, zend_bool implicit TSRMLS_DC) { + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret; + zend_bool persistent = s->persistent; DBG_ENTER("mysqlnd_stmt::dtor"); - DBG_INF_FMT("stmt=%p", stmt); + if (stmt) { - MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: - STAT_STMT_CLOSE_EXPLICIT); + DBG_INF_FMT("stmt=%p", stmt); - ret = stmt->m->net_close(stmt, implicit TSRMLS_CC); - mnd_efree(stmt); + MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT: + STAT_STMT_CLOSE_EXPLICIT); + + ret = s->m->net_close(s, implicit TSRMLS_CC); + mnd_pefree(stmt, persistent); + } + mnd_pefree(s, persistent); DBG_INF(ret == PASS? "PASS":"FAIL"); DBG_RETURN(ret); @@ -2021,6 +2218,59 @@ MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit /* }}} */ +/* {{{ mysqlnd_stmt::alloc_param_bind */ +static MYSQLND_PARAM_BIND * +MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind)(MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + DBG_ENTER("mysqlnd_stmt::alloc_param_bind"); + if (!stmt) { + DBG_RETURN(NULL); + } + DBG_RETURN(mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent)); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::alloc_result_bind */ +static MYSQLND_RESULT_BIND * +MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind)(MYSQLND_STMT * const s TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + DBG_ENTER("mysqlnd_stmt::alloc_result_bind"); + if (!stmt) { + DBG_RETURN(NULL); + } + DBG_RETURN(mnd_pecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND), stmt->persistent)); +} +/* }}} */ + + +/* {{{ param_bind::free_parameter_bind */ +PHPAPI void +MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind)(MYSQLND_STMT * const s, MYSQLND_PARAM_BIND * param_bind TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (stmt) { + mnd_pefree(param_bind, stmt->persistent); + } +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::free_result_bind */ +PHPAPI void +MYSQLND_METHOD(mysqlnd_stmt, free_result_bind)(MYSQLND_STMT * const s, MYSQLND_RESULT_BIND * result_bind TSRMLS_DC) +{ + MYSQLND_STMT_DATA * stmt = s? s->data:NULL; + if (stmt) { + mnd_pefree(result_bind, stmt->persistent); + } +} +/* }}} */ + + + MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, prepare), MYSQLND_METHOD(mysqlnd_stmt, execute), @@ -2040,10 +2290,8 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, bind_parameters), MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter), MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param), - MYSQLND_METHOD(mysqlnd_stmt, set_param_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, bind_result), MYSQLND_METHOD(mysqlnd_stmt, bind_one_result), - MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, send_long_data), MYSQLND_METHOD(mysqlnd_stmt, param_metadata), MYSQLND_METHOD(mysqlnd_stmt, result_metadata), @@ -2061,7 +2309,16 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt) MYSQLND_METHOD(mysqlnd_stmt, sqlstate), MYSQLND_METHOD(mysqlnd_stmt, attr_get), - MYSQLND_METHOD(mysqlnd_stmt, attr_set), + MYSQLND_METHOD(mysqlnd_stmt, attr_set), + + + MYSQLND_METHOD(mysqlnd_stmt, alloc_param_bind), + MYSQLND_METHOD(mysqlnd_stmt, alloc_result_bind), + MYSQLND_METHOD(mysqlnd_stmt, free_parameter_bind), + MYSQLND_METHOD(mysqlnd_stmt, free_result_bind), + MYSQLND_METHOD(mysqlnd_stmt, server_status), + mysqlnd_stmt_execute_generate_request, + mysqlnd_stmt_execute_parse_response MYSQLND_CLASS_METHODS_END; @@ -2069,28 +2326,47 @@ MYSQLND_CLASS_METHODS_END; MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) { size_t alloc_size = sizeof(MYSQLND_STMT) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_STMT *stmt = mnd_ecalloc(1, alloc_size); + MYSQLND_STMT * ret = mnd_pecalloc(1, alloc_size, conn->persistent); + MYSQLND_STMT_DATA * stmt = NULL; DBG_ENTER("_mysqlnd_stmt_init"); - DBG_INF_FMT("stmt=%p", stmt); - - stmt->m = mysqlnd_stmt_methods; - stmt->state = MYSQLND_STMT_INITTED; - stmt->execute_cmd_buffer.length = 4096; - stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length); + do { + if (!ret) { + break; + } + ret->m = mysqlnd_stmt_methods; + ret->persistent = conn->persistent; - stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; - /* - Mark that we reference the connection, thus it won't be - be destructed till there is open statements. The last statement - or normal query result will close it then. - */ - stmt->conn = conn->m->get_reference(conn TSRMLS_CC); + stmt = ret->data = mnd_pecalloc(1, sizeof(MYSQLND_STMT_DATA), conn->persistent); + DBG_INF_FMT("stmt=%p", stmt); + if (!stmt) { + break; + } + stmt->persistent = conn->persistent; + stmt->state = MYSQLND_STMT_INITTED; + stmt->execute_cmd_buffer.length = 4096; + stmt->execute_cmd_buffer.buffer = mnd_pemalloc(stmt->execute_cmd_buffer.length, stmt->persistent); + if (!stmt->execute_cmd_buffer.buffer) { + break; + } - stmt->m->set_param_bind_dtor(stmt, mysqlnd_efree_param_bind_dtor TSRMLS_CC); - stmt->m->set_result_bind_dtor(stmt, mysqlnd_efree_result_bind_dtor TSRMLS_CC); + stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS; + /* + Mark that we reference the connection, thus it won't be + be destructed till there is open statements. The last statement + or normal query result will close it then. + */ + stmt->conn = conn->m->get_reference(conn TSRMLS_CC); - DBG_RETURN(stmt); + DBG_RETURN(ret); + } while (0); + + SET_OOM_ERROR(conn->error_info); + if (ret) { + ret->m->dtor(ret, TRUE TSRMLS_CC); + ret = NULL; + } + DBG_RETURN(NULL); } /* }}} */ @@ -2108,23 +2384,6 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, u /* }}} */ -/* {{{ mysqlnd_efree_param_bind_dtor */ -PHPAPI void -mysqlnd_efree_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind TSRMLS_DC) -{ - mnd_efree(param_bind); -} -/* }}} */ - - -/* {{{ mysqlnd_efree_result_bind_dtor */ -PHPAPI void -mysqlnd_efree_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind TSRMLS_DC) -{ - mnd_efree(result_bind); -} -/* }}} */ - /* {{{ _mysqlnd_init_ps_subsystem */ void _mysqlnd_init_ps_subsystem() { @@ -2133,6 +2392,7 @@ void _mysqlnd_init_ps_subsystem() } /* }}} */ + /* {{{ mysqlnd_conn_get_methods */ PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods() { @@ -2140,6 +2400,7 @@ PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods() } /* }}} */ + /* {{{ mysqlnd_conn_set_methods */ PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods *methods) { |