summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_ps.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps.c')
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c411
1 files changed, 303 insertions, 108 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 59e9a96f63..acacee22ef 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -57,7 +57,10 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
zend_bool *fetched_anything TSRMLS_DC);
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
+static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, uint 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 *
@@ -100,7 +103,9 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
result->type = MYSQLND_RES_PS_BUF;
result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered;
result->m.fetch_lengths = NULL;/* makes no sense */
- result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ if (!result->zval_cache) {
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ }
/* Create room for 'next_extend' rows */
@@ -167,7 +172,9 @@ MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt
result->type = MYSQLND_RES_PS_BUF;
result->m.fetch_row = mysqlnd_fetch_stmt_row_buffered;
result->m.fetch_lengths = NULL;/* makes no sense */
- result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ if (!result->zval_cache) {
+ result->zval_cache = mysqlnd_palloc_get_thd_cache_reference(conn->zval_cache);
+ }
/* Create room for 'next_extend' rows */
@@ -248,6 +255,45 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
/* }}} */
+/* {{{ mysqlnd_stmt::more_results */
+static zend_bool
+MYSQLND_METHOD(mysqlnd_stmt, more_results)(const MYSQLND_STMT * stmt TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::more_results");
+ /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
+ DBG_RETURN((stmt->conn && (stmt->conn->upsert_status.server_status &
+ SERVER_MORE_RESULTS_EXISTS))?
+ TRUE:
+ FALSE);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt::next_result */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * stmt TSRMLS_DC)
+{
+ MYSQLND *conn = stmt->conn;
+
+ DBG_ENTER("mysqlnd_stmt::next_result");
+ 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)
+ {
+ DBG_RETURN(FAIL);
+ }
+
+ /* Free space for next result */
+ mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+
+ DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC));
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt_skip_metadata */
static enum_func_status
mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC)
@@ -399,7 +445,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co
/* Allocate the result now as it is needed for the reading of metadata */
stmt_to_prepare->result = result;
- result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn);
+ result->conn = stmt_to_prepare->conn->m->get_reference(stmt_to_prepare->conn TSRMLS_CC);
result->type = MYSQLND_RES_PS_BUF;
@@ -411,7 +457,7 @@ MYSQLND_METHOD(mysqlnd_stmt, prepare)(MYSQLND_STMT * const stmt, const char * co
if (stmt_to_prepare != stmt) {
/* Free old buffers, binding and resources on server */
- stmt->m->close(stmt, TRUE TSRMLS_CC);
+ stmt->m->net_close(stmt, TRUE TSRMLS_CC);
memcpy(stmt, stmt_to_prepare, sizeof(MYSQLND_STMT));
@@ -434,6 +480,92 @@ fail:
/* }}} */
+/* {{{ mysqlnd_stmt_execute_parse_response */
+static enum_func_status
+mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const stmt TSRMLS_DC)
+{
+ enum_func_status ret;
+ MYSQLND *conn = stmt->conn;
+
+ DBG_ENTER("mysqlnd_stmt_execute_parse_response");
+
+ CONN_SET_STATE(conn, CONN_QUERY_SENT);
+
+ ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);
+ if (ret == FAIL) {
+ stmt->error_info = conn->error_info;
+ stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
+ if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
+ /* close the statement here, the connection has been closed */
+ }
+ stmt->state = MYSQLND_STMT_PREPARED;
+ } else {
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+ stmt->send_types_to_server = 0;
+ stmt->upsert_status = conn->upsert_status;
+ stmt->state = MYSQLND_STMT_EXECUTED;
+ if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
+ DBG_INF("PASS");
+ DBG_RETURN(PASS);
+ }
+
+ stmt->result->type = MYSQLND_RES_PS_BUF;
+ if (!stmt->result->conn) {
+ /*
+ For SHOW we don't create (bypasses PS in server)
+ a result set at prepare and thus a connection was missing
+ */
+ stmt->result->conn = stmt->conn->m->get_reference(stmt->conn TSRMLS_CC);
+ }
+
+ /* Update stmt->field_count as SHOW sets it to 0 at prepare */
+ stmt->field_count = stmt->result->field_count = conn->field_count;
+ stmt->result->lengths = NULL;
+ if (stmt->field_count) {
+ stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
+ /*
+ We need to set this because the user might not call
+ 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,
+ stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
+
+ if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
+ stmt->cursor_exists = TRUE;
+ CONN_SET_STATE(conn, CONN_READY);
+ /* Only cursor read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
+ /*
+ We have asked for CURSOR but got no cursor, because the condition
+ above is not fulfilled. Then...
+
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+ /* preferred is buffered read */
+ stmt->default_rset_handler = stmt->m->store_result;
+ DBG_INF("store_result");
+ } else {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = stmt->m->use_result;
+ DBG_INF("use_result");
+ }
+ }
+ }
+
+ DBG_INF(ret == PASS? "PASS":"FAIL");
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt::execute */
static enum_func_status
MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
@@ -451,6 +583,12 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
SET_ERROR_AFF_ROWS(stmt->conn);
if (stmt->state > MYSQLND_STMT_PREPARED && stmt->field_count) {
+ /*
+ We don need to copy the data from the buffers which we will clean.
+ Because it has already been copied. See
+ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
if (stmt->result_bind &&
stmt->result_zvals_separated_once == TRUE &&
stmt->state >= MYSQLND_STMT_USER_FETCHING)
@@ -474,6 +612,29 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
}
}
+#endif
+
+ /*
+ If right after execute() 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");
+ /* Do implicit use_result and then flush the result */
+ stmt->default_rset_handler = stmt->m->use_result;
+ stmt->default_rset_handler(stmt TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_WAITING_USE_OR_STORE) {
+ DBG_INF("skipping result");
+ /* Flush if anything is left and unbuffered set */
+ stmt->result->m.skip_result(stmt->result TSRMLS_CC);
+ }
+
+ if (stmt->state > MYSQLND_STMT_PREPARED) {
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
/*
Executed, but the user hasn't started to fetch
@@ -534,79 +695,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
}
stmt->execute_count++;
- CONN_SET_STATE(conn, CONN_QUERY_SENT);
-
- ret = mysqlnd_query_read_result_set_header(stmt->conn, stmt TSRMLS_CC);
- if (ret == FAIL) {
- stmt->error_info = conn->error_info;
- stmt->upsert_status.affected_rows = conn->upsert_status.affected_rows;
- if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
- /* close the statement here, the connection has been closed */
- }
- stmt->state = MYSQLND_STMT_PREPARED;
- } else {
- SET_EMPTY_ERROR(stmt->error_info);
- SET_EMPTY_ERROR(stmt->conn->error_info);
- stmt->send_types_to_server = 0;
- stmt->upsert_status = conn->upsert_status;
- stmt->state = MYSQLND_STMT_EXECUTED;
- if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
- DBG_INF("PASS");
- DBG_RETURN(PASS);
- }
-
- stmt->result->type = MYSQLND_RES_PS_BUF;
- if (!stmt->result->conn) {
- /*
- For SHOW we don't create (bypasses PS in server)
- a result set at prepare and thus a connection was missing
- */
- stmt->result->conn = stmt->conn->m->get_reference(stmt->conn);
- }
-
- /* Update stmt->field_count as SHOW sets it to 0 at prepare */
- stmt->field_count = stmt->result->field_count = conn->field_count;
- stmt->result->lengths = NULL;
- if (stmt->field_count) {
- stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
- /*
- We need to set this because the user might not call
- 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,
- stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS);
-
- if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) {
- stmt->cursor_exists = TRUE;
- CONN_SET_STATE(conn, CONN_READY);
- /* Only cursor read */
- stmt->default_rset_handler = stmt->m->use_result;
- DBG_INF("use_result");
- } else if (stmt->flags & CURSOR_TYPE_READ_ONLY) {
- /*
- We have asked for CURSOR but got no cursor, because the condition
- above is not fulfilled. Then...
-
- This is a single-row result set, a result set with no rows, EXPLAIN,
- SHOW VARIABLES, or some other command which either a) bypasses the
- cursors framework in the server and writes rows directly to the
- network or b) is more efficient if all (few) result set rows are
- precached on client and server's resources are freed.
- */
- /* preferred is buffered read */
- stmt->default_rset_handler = stmt->m->store_result;
- DBG_INF("store_result");
- } else {
- /* preferred is unbuffered read */
- stmt->default_rset_handler = stmt->m->use_result;
- DBG_INF("use_result");
- }
- }
- }
-
- DBG_INF(ret == PASS? "PASS":"FAIL");
- DBG_RETURN(ret);
+ DBG_RETURN(mysqlnd_stmt_execute_parse_response(stmt TSRMLS_CC));
}
/* }}} */
@@ -661,7 +750,9 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f
for (i = 0; i < result->field_count; i++) {
/* Clean what we copied last time */
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
- zval_dtor(stmt->result_bind[i].zv);
+ if (stmt->result_bind[i].zv) {
+ zval_dtor(stmt->result_bind[i].zv);
+ }
#endif
/* copy the type */
if (stmt->result_bind[i].bound == TRUE) {
@@ -978,7 +1069,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 s_status=%d warns=%d eof=%d",
+ DBG_INF_FMT("ret=%s fetched=%d server_status=%d warnings=%d eof=%d",
ret == PASS? "PASS":"FAIL", *fetched_anything,
row_packet->server_status, row_packet->warning_count,
result->unbuf->eof_reached);
@@ -1079,8 +1170,13 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC)
DBG_INF("skipping result");
stmt->result->m.skip_result(stmt->result TSRMLS_CC);
}
+
/* Now the line should be free, if it wasn't */
+ DBG_INF("freeing result");
+ /* free_result() doesn't actually free stmt->result but only the buffers */
+ stmt->m->free_result(stmt TSRMLS_CC);
+
int4store(cmd_buf, stmt->stmt_id);
if (CONN_GET_STATE(conn) == CONN_READY &&
FAIL == (ret = mysqlnd_simple_command(conn, COM_STMT_RESET, (char *)cmd_buf,
@@ -1365,8 +1461,6 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
DBG_ENTER("mysqlnd_stmt::bind_result");
DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count);
- SET_EMPTY_ERROR(stmt->error_info);
- SET_EMPTY_ERROR(stmt->conn->error_info);
if (stmt->state < MYSQLND_STMT_PREPARED) {
SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared);
@@ -1377,6 +1471,9 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
DBG_RETURN(FAIL);
}
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
if (stmt->field_count) {
uint i = 0;
@@ -1386,7 +1483,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt,
}
mysqlnd_stmt_separate_result_bind(stmt TSRMLS_CC);
-
+ stmt->result_zvals_separated_once = FALSE;
stmt->result_bind = result_bind;
for (i = 0; i < stmt->field_count; i++) {
/* Prevent from freeing */
@@ -1407,6 +1504,50 @@ 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, uint param_no TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt::bind_result");
+ 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);
+ DBG_ERR("not prepared");
+ DBG_RETURN(FAIL);
+ }
+
+ if (param_no < 0 || param_no >= stmt->field_count) {
+ SET_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, UNKNOWN_SQLSTATE, "Invalid parameter number");
+ DBG_ERR("invalid param_no");
+ DBG_RETURN(FAIL);
+ }
+
+ SET_EMPTY_ERROR(stmt->error_info);
+ SET_EMPTY_ERROR(stmt->conn->error_info);
+
+ if (stmt->field_count) {
+ mysqlnd_stmt_separate_one_result_bind(stmt, param_no TSRMLS_CC);
+ /* Guaranteed is that stmt->result_bind is NULL */
+ if (!stmt->result_bind) {
+ stmt->result_bind = ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND));
+ } else {
+ stmt->result_bind = erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND));
+ }
+ 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");
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt::set_bind_result_dtor */
static void
MYSQLND_METHOD(mysqlnd_stmt, set_result_bind_dtor)(MYSQLND_STMT * const stmt,
@@ -1613,7 +1754,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt,
/* {{{ mysqlnd_stmt::attr_get */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt,
+MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const stmt,
enum mysqlnd_stmt_attr attr_type,
void * const value TSRMLS_DC)
{
@@ -1638,7 +1779,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(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)
@@ -1651,6 +1792,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
DBG_RETURN(PASS);
}
+ /*
+ If right after execute() 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");
/* Do implicit use_result and then flush the result */
@@ -1672,8 +1817,10 @@ MYSQLND_METHOD(mysqlnd_stmt, free_result)(MYSQLND_STMT * const stmt TSRMLS_DC)
stmt->result->m.free_result_buffers(stmt->result TSRMLS_CC);
}
- /* As the buffers have been freed, we should go back to PREPARED */
- stmt->state = MYSQLND_STMT_PREPARED;
+ if (stmt->state > MYSQLND_STMT_PREPARED) {
+ /* As the buffers have been freed, we should go back to PREPARED */
+ stmt->state = MYSQLND_STMT_PREPARED;
+ }
/* Line is free! */
CONN_SET_STATE(stmt->conn, CONN_READY);
@@ -1737,9 +1884,55 @@ 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, uint param_no TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
+ DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%d",
+ stmt->stmt_id, stmt->result_bind, stmt->field_count, param_no);
+
+ if (!stmt->result_bind) {
+ DBG_VOID_RETURN;
+ }
+
+ /*
+ Because only the bound variables can point to our internal buffers, then
+ separate or free only them. Free is possible because the user could have
+ lost reference.
+ */
+ /* 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));
+ /*
+ We have to separate the actual zval value of the bound
+ variable from our allocated zvals or we will face double-free
+ */
+ if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) {
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ zval_copy_ctor(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ } else {
+ /*
+ If it is a string, what is pointed will be freed
+ later in free_result(). We need to remove the variable to
+ which the user has lost reference.
+ */
+#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
+ ZVAL_NULL(stmt->result_bind[param_no].zv);
+#endif
+ zval_ptr_dtor(&stmt->result_bind[param_no].zv);
+ }
+ }
+
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_internal_free_stmt_content */
static
-void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
+void mysqlnd_internal_free_stmt_content(MYSQLND_STMT * const stmt TSRMLS_DC)
{
DBG_ENTER("mysqlnd_internal_free_stmt_content");
DBG_INF_FMT("stmt=%lu param_bind=%p param_count=%u",
@@ -1778,24 +1971,15 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC)
stmt->result->m.free_result_internal(stmt->result TSRMLS_CC);
stmt->result = NULL;
}
- if (stmt->cmd_buffer.buffer) {
- mnd_efree(stmt->cmd_buffer.buffer);
- stmt->cmd_buffer.buffer = NULL;
- }
-
- if (stmt->conn) {
- stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
- stmt->conn = NULL;
- }
DBG_VOID_RETURN;
}
/* }}} */
-/* {{{ mysqlnd_stmt::close */
+/* {{{ mysqlnd_stmt::net_close */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
{
MYSQLND * conn = stmt->conn;
zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */];
@@ -1854,16 +2038,25 @@ MYSQLND_METHOD(mysqlnd_stmt, close)(MYSQLND_STMT * const stmt, zend_bool implici
MYSQLND_INC_CONN_STATISTIC(&conn->stats, stat);
}
+ if (stmt->execute_cmd_buffer.buffer) {
+ mnd_efree(stmt->execute_cmd_buffer.buffer);
+ stmt->execute_cmd_buffer.buffer = NULL;
+ }
+
mysqlnd_internal_free_stmt_content(stmt TSRMLS_CC);
+ if (stmt->conn) {
+ stmt->conn->m->free_reference(stmt->conn TSRMLS_CC);
+ stmt->conn = NULL;
+ }
+
DBG_RETURN(PASS);
}
/* }}} */
-
/* {{{ mysqlnd_stmt::dtor */
static enum_func_status
-MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool implicit TSRMLS_DC)
{
enum_func_status ret;
@@ -1873,9 +2066,8 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor)(MYSQLND_STMT * const stmt, zend_bool
MYSQLND_INC_GLOBAL_STATISTIC(implicit == TRUE? STAT_STMT_CLOSE_IMPLICIT:
STAT_STMT_CLOSE_EXPLICIT);
- if (PASS == (ret = stmt->m->close(stmt, implicit TSRMLS_CC))) {
- mnd_efree(stmt);
- }
+ ret = stmt->m->net_close(stmt, implicit TSRMLS_CC);
+ mnd_efree(stmt);
DBG_INF(ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
@@ -1891,11 +2083,13 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
MYSQLND_METHOD(mysqlnd_stmt, store_result),
MYSQLND_METHOD(mysqlnd_stmt, background_store_result),
MYSQLND_METHOD(mysqlnd_stmt, get_result),
+ MYSQLND_METHOD(mysqlnd_stmt, more_results),
+ MYSQLND_METHOD(mysqlnd_stmt, next_result),
MYSQLND_METHOD(mysqlnd_stmt, free_result),
MYSQLND_METHOD(mysqlnd_stmt, data_seek),
MYSQLND_METHOD(mysqlnd_stmt, reset),
- MYSQLND_METHOD(mysqlnd_stmt, close),
- MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, dtor),
+ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, net_close),
+ MYSQLND_METHOD(mysqlnd_stmt, dtor),
MYSQLND_METHOD(mysqlnd_stmt, fetch),
@@ -1904,6 +2098,7 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = {
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),
@@ -1936,8 +2131,8 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
stmt->m = &mysqlnd_stmt_methods;
stmt->state = MYSQLND_STMT_INITTED;
- stmt->cmd_buffer.length = 4096;
- stmt->cmd_buffer.buffer = mnd_emalloc(stmt->cmd_buffer.length);
+ stmt->execute_cmd_buffer.length = 4096;
+ stmt->execute_cmd_buffer.buffer = mnd_emalloc(stmt->execute_cmd_buffer.length);
stmt->prefetch_rows = MYSQLND_DEFAULT_PREFETCH_ROWS;
/*
@@ -1945,7 +2140,7 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC)
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);
+ stmt->conn = conn->m->get_reference(conn TSRMLS_CC);
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);