diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps.c')
| -rw-r--r-- | ext/mysqlnd/mysqlnd_ps.c | 260 |
1 files changed, 197 insertions, 63 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 7679b72169..59e9a96f63 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -56,7 +56,7 @@ enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC); -void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); +static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); /* {{{ mysqlnd_stmt::store_result */ @@ -78,8 +78,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); - DBG_RETURN(res); + DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -135,7 +134,7 @@ MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt MYSQLND_RES *result; zend_bool to_cache = FALSE; - DBG_ENTER("mysqlnd_stmt::store_result"); + DBG_ENTER("mysqlnd_stmt::background_store_result"); DBG_INF_FMT("stmt=%lu", stmt->stmt_id); /* be compliant with libmysql - NULL will turn */ @@ -198,6 +197,7 @@ MYSQLND_METHOD(mysqlnd_stmt, background_store_result)(MYSQLND_STMT * const stmt } /* }}} */ + /* {{{ mysqlnd_stmt::get_result */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) @@ -215,8 +215,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const stmt TSRMLS_DC) if (stmt->cursor_exists) { /* Silently convert buffered to unbuffered, for now */ - MYSQLND_RES * res = stmt->m->use_result(stmt TSRMLS_CC); - DBG_RETURN(res); + DBG_RETURN(stmt->m->use_result(stmt TSRMLS_CC)); } /* Nothing to store for UPSERT/LOAD DATA*/ @@ -255,6 +254,7 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) { /* Follows parameter metadata, we have just to skip it, as libmysql does */ unsigned int i = 0; + enum_func_status ret = PASS; php_mysql_packet_res_field field_packet; DBG_ENTER("mysqlnd_stmt_skip_metadata"); @@ -264,13 +264,13 @@ mysqlnd_stmt_skip_metadata(MYSQLND_STMT *stmt TSRMLS_DC) field_packet.skip_parsing = TRUE; for (;i < stmt->param_count; i++) { if (FAIL == PACKET_READ_ALLOCA(field_packet, stmt->conn)) { - PACKET_FREE_ALLOCA(field_packet); - DBG_RETURN(FAIL); + ret = FAIL; + break; } } PACKET_FREE_ALLOCA(field_packet); - DBG_RETURN(PASS); + DBG_RETURN(ret); } /* }}} */ @@ -280,19 +280,21 @@ static enum_func_status mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) { php_mysql_packet_prepare_response prepare_resp; + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_stmt_read_prepare_response"); DBG_INF_FMT("stmt=%lu", stmt->stmt_id); PACKET_INIT_ALLOCA(prepare_resp, PROT_PREPARE_RESP_PACKET); if (FAIL == PACKET_READ_ALLOCA(prepare_resp, stmt->conn)) { - PACKET_FREE_ALLOCA(prepare_resp); - return FAIL; + ret = FAIL; + goto done; } if (0xFF == prepare_resp.error_code) { stmt->error_info = stmt->conn->error_info = prepare_resp.error_info; - return FAIL; + ret = FAIL; + goto done; } stmt->stmt_id = prepare_resp.stmt_id; @@ -301,7 +303,8 @@ mysqlnd_stmt_read_prepare_response(MYSQLND_STMT *stmt TSRMLS_DC) stmt->param_count = prepare_resp.param_count; PACKET_FREE_ALLOCA(prepare_resp); - DBG_RETURN(PASS); +done: + DBG_RETURN(ret); } /* }}} */ @@ -487,13 +490,31 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC) DBG_RETURN(FAIL); } - if (stmt->param_count && !stmt->param_bind) { - SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, - "No data supplied for parameters in prepared statement"); - DBG_INF("FAIL"); - DBG_RETURN(FAIL); + if (stmt->param_count) { + uint i, not_bound = 0; + if (!stmt->param_bind) { + SET_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, UNKNOWN_SQLSTATE, + "No data supplied for parameters in prepared statement"); + DBG_INF("FAIL"); + DBG_RETURN(FAIL); + } + for (i = 0; i < stmt->param_count; i++) { + if (stmt->param_bind[i].zv == NULL) { + not_bound++; + } + } + if (not_bound) { + char * msg; + spprintf(&msg, 0, "No data supplied for %d 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); + } + 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 ! */ @@ -553,7 +574,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(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\n", stmt->upsert_status.server_status, + 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) { @@ -595,10 +616,9 @@ enum_func_status mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { - unsigned int i; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; - uint field_count = result->meta->field_count; MYSQLND_RES_BUFFERED *set = result->stored_data; + uint field_count = result->meta->field_count; DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); DBG_INF_FMT("stmt=%lu", stmt->stmt_id); @@ -609,6 +629,8 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f { /* The user could have skipped binding - don't crash*/ if (stmt->result_bind) { + unsigned int i; + MYSQLND_RES_METADATA * meta = result->meta; zval **current_row = set->data_cursor; if (NULL == current_row[0]) { @@ -616,8 +638,8 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f set->initialized_rows++; result->m.row_decoder(set->row_buffers[row_num], current_row, - result->meta->field_count, - result->meta->fields, + meta->field_count, + meta->fields, result->conn TSRMLS_CC); if (stmt->update_max_length) { for (i = 0; i < result->field_count; i++) { @@ -628,8 +650,8 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f */ if (Z_TYPE_P(current_row[i]) >= IS_STRING) { unsigned long len = Z_STRLEN_P(current_row[i]); - if (result->meta->fields[i].max_length < len) { - result->meta->fields[i].max_length = len; + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; } } } @@ -687,7 +709,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int { enum_func_status ret; MYSQLND_STMT *stmt = (MYSQLND_STMT *) param; - unsigned int i, field_count = result->field_count; php_mysql_packet_row *row_packet = result->row_packet; DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); @@ -712,6 +733,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int mysqlnd_unbuffered_free_last_data() before it. The function returns always true. */ 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; @@ -966,7 +988,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla /* {{{ mysqlnd_stmt::fetch */ -PHPAPI enum_func_status +static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const stmt, zend_bool * const fetched_anything TSRMLS_DC) { @@ -1021,7 +1043,6 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) { enum_func_status ret = PASS; - MYSQLND * conn = stmt->conn; zend_uchar cmd_buf[STMT_ID_LENGTH /* statement id */]; DBG_ENTER("mysqlnd_stmt::reset"); @@ -1031,6 +1052,7 @@ MYSQLND_METHOD(mysqlnd_stmt, reset)(MYSQLND_STMT * const stmt TSRMLS_DC) SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->stmt_id) { + MYSQLND * conn = stmt->conn; if (stmt->param_bind) { unsigned int i; DBG_INF("resetting long data"); @@ -1084,7 +1106,6 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned enum_func_status ret = FAIL; MYSQLND * conn = stmt->conn; zend_uchar *cmd_buf; - size_t packet_len; enum php_mysqlnd_server_command cmd = COM_STMT_SEND_LONG_DATA; DBG_ENTER("mysqlnd_stmt::send_long_data"); @@ -1126,6 +1147,7 @@ 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); @@ -1178,19 +1200,20 @@ MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const stmt, unsigned /* }}} */ -/* {{{ _mysqlnd_stmt_bind_param */ +/* {{{ mysqlnd_stmt::bind_parameters */ static enum_func_status -MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, - MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const stmt, + MYSQLND_PARAM_BIND * const param_bind TSRMLS_DC) { - unsigned int i = 0; - DBG_ENTER("mysqlnd_stmt::bind_param"); 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); + } DBG_RETURN(FAIL); } @@ -1198,6 +1221,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, SET_EMPTY_ERROR(stmt->conn->error_info); if (stmt->param_count) { + unsigned int i = 0; + if (!param_bind) { SET_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, "Re-binding (still) not supported"); @@ -1210,29 +1235,28 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, Forbid for now re-binding!! */ for (i = 0; i < stmt->param_count; i++) { - /* For BLOBS zv is NULL */ + /* + We may have the last reference, then call zval_ptr_dtor() + or we may leak memory. + Switching from bind_one_parameter to bind_parameters may result in zv being NULL + */ if (stmt->param_bind[i].zv) { - /* - We may have the last reference, then call zval_ptr_dtor() - or we may leak memory. - */ zval_ptr_dtor(&stmt->param_bind[i].zv); - stmt->param_bind[i].zv = NULL; } } - mnd_efree(stmt->param_bind); + if (stmt->param_bind != param_bind && stmt->param_bind_dtor) { + stmt->param_bind_dtor(stmt->param_bind); + } } 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); - if (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB) { - /* Prevent from freeing */ - Z_ADDREF_P(stmt->param_bind[i].zv); - /* Don't update is_ref, or we will leak during conversion */ - } else { - stmt->param_bind[i].zv = NULL; + /* Prevent from freeing */ + /* Don't update is_ref, or we will leak during conversion */ + Z_ADDREF_P(stmt->param_bind[i].zv); + if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) { stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } } @@ -1244,7 +1268,58 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_param)(MYSQLND_STMT * const stmt, /* }}} */ -/* {{{ _mysqlnd_stmt_refresh_bind_param */ +/* {{{ mysqlnd_stmt::bind_one_parameter */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const stmt, unsigned int param_no, + zval * const zv, zend_uchar type TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_stmt::bind_one_parameter"); + DBG_INF_FMT("stmt=%lu param_no=%d param_count=%u type=%d", + stmt->stmt_id, param_no, stmt->param_count, type); + + 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->param_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->param_count) { + if (!stmt->param_bind) { + stmt->param_bind = ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND)); + } + + /* Prevent from freeing */ + /* Don't update is_ref, or we will leak during conversion */ + Z_ADDREF_P(zv); + DBG_INF("Binding"); + /* Release what we had, if we had */ + if (stmt->param_bind[param_no].zv) { + zval_ptr_dtor(&stmt->param_bind[param_no].zv); + } + if (type == MYSQL_TYPE_LONG_BLOB) { + /* The client will use stmt_send_long_data */ + stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; + } + stmt->param_bind[param_no].zv = zv; + stmt->param_bind[param_no].type = type; + + stmt->send_types_to_server = 1; + } + DBG_INF("PASS"); + DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_stmt::refresh_bind_param */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, refresh_bind_param)(MYSQLND_STMT * const stmt TSRMLS_DC) { @@ -1269,13 +1344,24 @@ 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) +{ + 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_RESULT_BIND * const result_bind TSRMLS_DC) { - uint i = 0; - DBG_ENTER("mysqlnd_stmt::bind_result"); DBG_INF_FMT("stmt=%lu field_count=%u", stmt->stmt_id, stmt->field_count); @@ -1284,14 +1370,16 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, if (stmt->state < MYSQLND_STMT_PREPARED) { SET_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, UNKNOWN_SQLSTATE, mysqlnd_stmt_not_prepared); - if (result_bind) { - mnd_efree(result_bind); + if (result_bind && stmt->result_bind_dtor) { + stmt->result_bind_dtor(result_bind); } DBG_ERR("not prepared"); DBG_RETURN(FAIL); } if (stmt->field_count) { + uint i = 0; + if (!result_bind) { DBG_ERR("no result bind passed"); DBG_RETURN(FAIL); @@ -1310,8 +1398,8 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, */ stmt->result_bind[i].bound = TRUE; } - } else if (result_bind) { - mnd_efree(result_bind); + } else if (result_bind && stmt->result_bind_dtor) { + stmt->result_bind_dtor(result_bind); } DBG_INF("PASS"); DBG_RETURN(PASS); @@ -1319,6 +1407,19 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const stmt, /* }}} */ +/* {{{ 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) +{ + 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 MYSQLND_METHOD(mysqlnd_stmt, insert_id)(const MYSQLND_STMT * const stmt) @@ -1483,7 +1584,8 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, case STMT_ATTR_CURSOR_TYPE: { if (val > (unsigned long) CURSOR_TYPE_READ_ONLY) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } stmt->flags = val; break; @@ -1493,7 +1595,8 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, val = MYSQLND_DEFAULT_PREFETCH_ROWS; } else if (val > 1) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); - return FAIL; + DBG_INF("FAIL"); + DBG_RETURN(FAIL); } stmt->prefetch_rows = val; break; @@ -1508,7 +1611,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const stmt, /* }}} */ -/* {{{ _mysqlnd_stmt_attr_get */ +/* {{{ mysqlnd_stmt::attr_get */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, attr_get)(MYSQLND_STMT * const stmt, enum mysqlnd_stmt_attr attr_type, @@ -1624,7 +1727,9 @@ void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC) } } } - mnd_efree(stmt->result_bind); + if (stmt->result_bind_dtor) { + stmt->result_bind_dtor(stmt->result_bind); + } stmt->result_bind = NULL; DBG_VOID_RETURN; @@ -1649,13 +1754,17 @@ void mysqlnd_internal_free_stmt_content(MYSQLND_STMT *stmt TSRMLS_DC) lost reference. */ for (i = 0; i < stmt->param_count; i++) { - /* For BLOBs zv is NULL */ + /* + If bind_one_parameter was used, but not everything was + bound and nothing was fetched, then some `zv` could be NULL + */ if (stmt->param_bind[i].zv) { zval_ptr_dtor(&stmt->param_bind[i].zv); } } - - mnd_efree(stmt->param_bind); + if (stmt->param_bind_dtor) { + stmt->param_bind_dtor(stmt->param_bind); + } stmt->param_bind = NULL; } @@ -1790,9 +1899,12 @@ struct st_mysqlnd_stmt_methods mysqlnd_stmt_methods = { MYSQLND_METHOD(mysqlnd_stmt, fetch), - MYSQLND_METHOD(mysqlnd_stmt, bind_param), + 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, set_result_bind_dtor), MYSQLND_METHOD(mysqlnd_stmt, send_long_data), MYSQLND_METHOD(mysqlnd_stmt, param_metadata), MYSQLND_METHOD(mysqlnd_stmt, result_metadata), @@ -1835,10 +1947,32 @@ MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC) */ stmt->conn = conn->m->get_reference(conn); + 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); + DBG_RETURN(stmt); } /* }}} */ + +/* {{{ mysqlnd_efree_param_bind_dtor */ +PHPAPI void +mysqlnd_efree_param_bind_dtor(MYSQLND_PARAM_BIND * param_bind) +{ + efree(param_bind); +} +/* }}} */ + + +/* {{{ mysqlnd_efree_result_bind_dtor */ +PHPAPI void +mysqlnd_efree_result_bind_dtor(MYSQLND_RESULT_BIND * result_bind) +{ + efree(result_bind); +} +/* }}} */ + + /* * Local variables: * tab-width: 4 |
