diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_ps.c | 411 |
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); |