diff options
Diffstat (limited to 'ext/pdo_mysql/mysql_statement.c')
-rw-r--r-- | ext/pdo_mysql/mysql_statement.c | 404 |
1 files changed, 158 insertions, 246 deletions
diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index beb559f0c1..871b7f9c7c 100644 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -1,7 +1,5 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | @@ -32,13 +30,34 @@ #ifdef PDO_USE_MYSQLND # define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_mysqlnd(stmt) -# define pdo_free_bound_result(res) zval_ptr_dtor(res.zv) #else # define pdo_mysql_stmt_execute_prepared(stmt) pdo_mysql_stmt_execute_prepared_libmysql(stmt) -# define pdo_free_bound_result(res) efree(res.buffer) #endif +static void pdo_mysql_free_result(pdo_mysql_stmt *S) +{ + if (S->result) { +#ifndef PDO_USE_MYSQLND + if (S->bound_result) { + /* We can't use stmt->column_count here, because it gets reset before the + * next_rowset handler is called. */ + unsigned column_count = mysql_num_fields(S->result); + for (unsigned i = 0; i < column_count; i++) { + efree(S->bound_result[i].buffer); + } + + efree(S->bound_result); + efree(S->out_null); + efree(S->out_length); + S->bound_result = NULL; + } +#endif + + mysql_free_result(S->result); + S->result = NULL; + } +} static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ { @@ -46,11 +65,8 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ PDO_DBG_ENTER("pdo_mysql_stmt_dtor"); PDO_DBG_INF_FMT("stmt=%p", S->stmt); - if (S->result) { - /* free the resource */ - mysql_free_result(S->result); - S->result = NULL; - } + + pdo_mysql_free_result(S); if (S->einfo.errmsg) { pefree(S->einfo.errmsg, stmt->dbh->is_persistent); S->einfo.errmsg = NULL; @@ -70,21 +86,9 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ if (S->in_length) { efree(S->in_length); } - - if (S->bound_result) - { - int i; - for (i = 0; i < stmt->column_count; i++) { - pdo_free_bound_result(S->bound_result[i]); - } - - efree(S->bound_result); - efree(S->out_null); - efree(S->out_length); - } #endif - if (!Z_ISUNDEF(stmt->database_object_handle) + if (!S->done && !Z_ISUNDEF(stmt->database_object_handle) && IS_OBJ_VALID(EG(objects_store).object_buckets[Z_OBJ_HANDLE(stmt->database_object_handle)]) && (!(OBJ_FLAGS(Z_OBJ(stmt->database_object_handle)) & IS_OBJ_FREE_CALLED))) { while (mysql_more_results(S->H->server)) { @@ -100,7 +104,7 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ } } -#if PDO_USE_MYSQLND +#ifdef PDO_USE_MYSQLND if (!S->stmt && S->current_data) { mnd_free(S->current_data); } @@ -113,9 +117,8 @@ static int pdo_mysql_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */ static void pdo_mysql_stmt_set_row_count(pdo_stmt_t *stmt) /* {{{ */ { - zend_long row_count; pdo_mysql_stmt *S = stmt->driver_data; - row_count = (zend_long) mysql_stmt_affected_rows(S->stmt); + zend_long row_count = (zend_long) mysql_stmt_affected_rows(S->stmt); if (row_count != (zend_long)-1) { stmt->row_count = row_count; } @@ -144,7 +147,7 @@ static int pdo_mysql_fill_stmt_from_result(pdo_stmt_t *stmt) /* {{{ */ } stmt->row_count = (zend_long) mysql_num_rows(S->result); - stmt->column_count = (int) mysql_num_fields(S->result); + php_pdo_stmt_set_column_count(stmt, (int) mysql_num_fields(S->result)); S->fields = mysql_fetch_fields(S->result); } else { /* this was a DML or DDL query (INSERT, UPDATE, DELETE, ... */ @@ -155,116 +158,129 @@ static int pdo_mysql_fill_stmt_from_result(pdo_stmt_t *stmt) /* {{{ */ } /* }}} */ -#ifndef PDO_USE_MYSQLND -static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */ -{ +static bool pdo_mysql_stmt_after_execute_prepared(pdo_stmt_t *stmt) { pdo_mysql_stmt *S = stmt->driver_data; pdo_mysql_db_handle *H = S->H; - PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_libmysql"); +#ifdef PDO_USE_MYSQLND + /* For SHOW/DESCRIBE and others the column/field count is not available before execute. */ + php_pdo_stmt_set_column_count(stmt, mysql_stmt_field_count(S->stmt)); + for (int i = 0; i < stmt->column_count; i++) { + mysqlnd_stmt_bind_one_result(S->stmt, i); + } - /* (re)bind the parameters */ - if (mysql_stmt_bind_param(S->stmt, S->params) || mysql_stmt_execute(S->stmt)) { - if (S->params) { - memset(S->params, 0, S->num_params * sizeof(MYSQL_BIND)); - } - pdo_mysql_error_stmt(stmt); - if (mysql_stmt_errno(S->stmt) == 2057) { - /* CR_NEW_STMT_METADATA makes the statement unusable */ - S->stmt = NULL; + S->result = mysqlnd_stmt_result_metadata(S->stmt); + if (S->result) { + S->fields = mysql_fetch_fields(S->result); + /* If buffered, pre-fetch all the data */ + if (H->buffered) { + if (mysql_stmt_store_result(S->stmt)) { + pdo_mysql_error_stmt(stmt); + return false; + } } - PDO_DBG_RETURN(0); } +#else + /* figure out the result set format, if any */ + S->result = mysql_stmt_result_metadata(S->stmt); + if (S->result) { + int calc_max_length = H->buffered && S->max_length == 1; + S->fields = mysql_fetch_fields(S->result); - if (!S->result) { - int i; - - /* figure out the result set format, if any */ - S->result = mysql_stmt_result_metadata(S->stmt); - if (S->result) { - int calc_max_length = H->buffered && S->max_length == 1; - S->fields = mysql_fetch_fields(S->result); - if (S->bound_result) { - int i; - for (i = 0; i < stmt->column_count; i++) { - efree(S->bound_result[i].buffer); - } - efree(S->bound_result); - efree(S->out_null); - efree(S->out_length); + php_pdo_stmt_set_column_count(stmt, (int)mysql_num_fields(S->result)); + S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND)); + S->out_null = ecalloc(stmt->column_count, sizeof(my_bool)); + S->out_length = ecalloc(stmt->column_count, sizeof(zend_ulong)); + + /* summon memory to hold the row */ + for (int i = 0; i < stmt->column_count; i++) { + if (calc_max_length && S->fields[i].type == FIELD_TYPE_BLOB) { + my_bool on = 1; + mysql_stmt_attr_set(S->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on); + calc_max_length = 0; + } + switch (S->fields[i].type) { + case FIELD_TYPE_INT24: + S->bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH + 1; + break; + case FIELD_TYPE_LONG: + S->bound_result[i].buffer_length = MAX_INT_WIDTH + 1; + break; + case FIELD_TYPE_LONGLONG: + S->bound_result[i].buffer_length = MAX_BIGINT_WIDTH + 1; + break; + case FIELD_TYPE_TINY: + S->bound_result[i].buffer_length = MAX_TINYINT_WIDTH + 1; + break; + case FIELD_TYPE_SHORT: + S->bound_result[i].buffer_length = MAX_SMALLINT_WIDTH + 1; + break; + default: + S->bound_result[i].buffer_length = + S->fields[i].max_length? S->fields[i].max_length: + S->fields[i].length; + /* work-around for longtext and alike */ + if (S->bound_result[i].buffer_length > H->max_buffer_size) { + S->bound_result[i].buffer_length = H->max_buffer_size; + } } - stmt->column_count = (int)mysql_num_fields(S->result); - S->bound_result = ecalloc(stmt->column_count, sizeof(MYSQL_BIND)); - S->out_null = ecalloc(stmt->column_count, sizeof(my_bool)); - S->out_length = ecalloc(stmt->column_count, sizeof(zend_ulong)); - - /* summon memory to hold the row */ - for (i = 0; i < stmt->column_count; i++) { - if (calc_max_length && S->fields[i].type == FIELD_TYPE_BLOB) { - my_bool on = 1; - mysql_stmt_attr_set(S->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on); - calc_max_length = 0; - } - switch (S->fields[i].type) { - case FIELD_TYPE_INT24: - S->bound_result[i].buffer_length = MAX_MEDIUMINT_WIDTH + 1; - break; - case FIELD_TYPE_LONG: - S->bound_result[i].buffer_length = MAX_INT_WIDTH + 1; - break; - case FIELD_TYPE_LONGLONG: - S->bound_result[i].buffer_length = MAX_BIGINT_WIDTH + 1; - break; - case FIELD_TYPE_TINY: - S->bound_result[i].buffer_length = MAX_TINYINT_WIDTH + 1; - break; - case FIELD_TYPE_SHORT: - S->bound_result[i].buffer_length = MAX_SMALLINT_WIDTH + 1; - break; - default: - S->bound_result[i].buffer_length = - S->fields[i].max_length? S->fields[i].max_length: - S->fields[i].length; - /* work-around for longtext and alike */ - if (S->bound_result[i].buffer_length > H->max_buffer_size) { - S->bound_result[i].buffer_length = H->max_buffer_size; - } - } + /* there are cases where the length reported by mysql is too short. + * eg: when describing a table that contains an enum column. Since + * we have no way of knowing the true length either, we'll bump up + * our buffer size to a reasonable size, just in case */ + if (S->fields[i].max_length == 0 && S->bound_result[i].buffer_length < 128 && MYSQL_TYPE_VAR_STRING) { + S->bound_result[i].buffer_length = 128; + } - /* there are cases where the length reported by mysql is too short. - * eg: when describing a table that contains an enum column. Since - * we have no way of knowing the true length either, we'll bump up - * our buffer size to a reasonable size, just in case */ - if (S->fields[i].max_length == 0 && S->bound_result[i].buffer_length < 128 && MYSQL_TYPE_VAR_STRING) { - S->bound_result[i].buffer_length = 128; - } + S->out_length[i] = 0; - S->out_length[i] = 0; + S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length); + S->bound_result[i].is_null = &S->out_null[i]; + S->bound_result[i].length = &S->out_length[i]; + S->bound_result[i].buffer_type = MYSQL_TYPE_STRING; + } - S->bound_result[i].buffer = emalloc(S->bound_result[i].buffer_length); - S->bound_result[i].is_null = &S->out_null[i]; - S->bound_result[i].length = &S->out_length[i]; - S->bound_result[i].buffer_type = MYSQL_TYPE_STRING; - } + if (mysql_stmt_bind_result(S->stmt, S->bound_result)) { + pdo_mysql_error_stmt(stmt); + PDO_DBG_RETURN(0); + } - if (mysql_stmt_bind_result(S->stmt, S->bound_result)) { + /* if buffered, pre-fetch all the data */ + if (H->buffered) { + if (mysql_stmt_store_result(S->stmt)) { pdo_mysql_error_stmt(stmt); PDO_DBG_RETURN(0); } - - /* if buffered, pre-fetch all the data */ - if (H->buffered) { - if (mysql_stmt_store_result(S->stmt)) { - pdo_mysql_error_stmt(stmt); - PDO_DBG_RETURN(0); - } - } } } +#endif pdo_mysql_stmt_set_row_count(stmt); - PDO_DBG_RETURN(1); + return true; +} + +#ifndef PDO_USE_MYSQLND +static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */ +{ + pdo_mysql_stmt *S = stmt->driver_data; + + PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_libmysql"); + + /* (re)bind the parameters */ + if (mysql_stmt_bind_param(S->stmt, S->params) || mysql_stmt_execute(S->stmt)) { + if (S->params) { + memset(S->params, 0, S->num_params * sizeof(MYSQL_BIND)); + } + pdo_mysql_error_stmt(stmt); + if (mysql_stmt_errno(S->stmt) == 2057) { + /* CR_NEW_STMT_METADATA makes the statement unusable */ + S->stmt = NULL; + } + PDO_DBG_RETURN(0); + } + + PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt)); } /* }}} */ #endif @@ -273,8 +289,6 @@ static int pdo_mysql_stmt_execute_prepared_libmysql(pdo_stmt_t *stmt) /* {{{ */ static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt) /* {{{ */ { pdo_mysql_stmt *S = stmt->driver_data; - pdo_mysql_db_handle *H = S->H; - int i; PDO_DBG_ENTER("pdo_mysql_stmt_execute_prepared_mysqlnd"); @@ -283,32 +297,7 @@ static int pdo_mysql_stmt_execute_prepared_mysqlnd(pdo_stmt_t *stmt) /* {{{ */ PDO_DBG_RETURN(0); } - if (S->result) { - /* TODO: add a test to check if we really have zvals here... */ - mysql_free_result(S->result); - S->result = NULL; - } - - /* for SHOW/DESCRIBE and others the column/field count is not available before execute */ - stmt->column_count = mysql_stmt_field_count(S->stmt); - for (i = 0; i < stmt->column_count; i++) { - mysqlnd_stmt_bind_one_result(S->stmt, i); - } - - S->result = mysqlnd_stmt_result_metadata(S->stmt); - if (S->result) { - S->fields = mysql_fetch_fields(S->result); - /* if buffered, pre-fetch all the data */ - if (H->buffered) { - if (mysql_stmt_store_result(S->stmt)) { - pdo_mysql_error_stmt(stmt); - PDO_DBG_RETURN(0); - } - } - } - - pdo_mysql_stmt_set_row_count(stmt); - PDO_DBG_RETURN(1); + PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt)); } /* }}} */ #endif @@ -320,16 +309,14 @@ static int pdo_mysql_stmt_execute(pdo_stmt_t *stmt) /* {{{ */ PDO_DBG_ENTER("pdo_mysql_stmt_execute"); PDO_DBG_INF_FMT("stmt=%p", S->stmt); + /* ensure that we free any previous unfetched results */ + pdo_mysql_free_result(S); + S->done = 0; + if (S->stmt) { PDO_DBG_RETURN(pdo_mysql_stmt_execute_prepared(stmt)); } - /* ensure that we free any previous unfetched results */ - if (S->result) { - mysql_free_result(S->result); - S->result = NULL; - } - if (mysql_real_query(H->server, stmt->active_query_string, stmt->active_query_stringlen) != 0) { pdo_mysql_error_stmt(stmt); PDO_DBG_RETURN(0); @@ -343,100 +330,28 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt) /* {{{ */ { pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; pdo_mysql_db_handle *H = S->H; -#if PDO_USE_MYSQLND - zend_long row_count; -#endif PDO_DBG_ENTER("pdo_mysql_stmt_next_rowset"); PDO_DBG_INF_FMT("stmt=%p", S->stmt); -#if PDO_USE_MYSQLND + /* ensure that we free any previous unfetched results */ + pdo_mysql_free_result(S); + if (S->stmt) { - if (!mysqlnd_stmt_more_results(S->stmt)) { - PDO_DBG_RETURN(0); - } - if (mysqlnd_stmt_next_result(S->stmt)) { + mysql_stmt_free_result(S->stmt); + if (mysql_stmt_next_result(S->stmt)) { pdo_mysql_error_stmt(stmt); + S->done = 1; PDO_DBG_RETURN(0); } - - if (!mysqlnd_stmt_more_results(S->stmt)) { - /* - MySQL gives us n + 1 result sets for - CALL proc() and n result sets returned by the proc itself. - Result set n + 1 is about the procedure call itself. - As the PDO emulation does not return it, we skip it as well - */ + PDO_DBG_RETURN(pdo_mysql_stmt_after_execute_prepared(stmt)); + } else { + if (mysql_next_result(H->server)) { + pdo_mysql_error_stmt(stmt); + S->done = 1; PDO_DBG_RETURN(0); } - - /* TODO - this code is stolen from execute() - see above */ - if (S->result) { - mysql_free_result(S->result); - S->result = NULL; - } - { - /* for SHOW/DESCRIBE and others the column/field count is not available before execute */ - int i; - - stmt->column_count = mysql_stmt_field_count(S->stmt); - for (i = 0; i < stmt->column_count; i++) { - mysqlnd_stmt_bind_one_result(S->stmt, i); - } - } - - S->result = mysqlnd_stmt_result_metadata(S->stmt); - if (S->result) { - S->fields = mysql_fetch_fields(S->result); - - /* if buffered, pre-fetch all the data */ - if (H->buffered) { - if (mysql_stmt_store_result(S->stmt)) { - pdo_mysql_error_stmt(stmt); - PDO_DBG_RETURN(0); - } - } - } - row_count = (zend_long) mysql_stmt_affected_rows(S->stmt); - if (row_count != (zend_long)-1) { - stmt->row_count = row_count; - } - PDO_DBG_RETURN(1); - } -#endif - -/* ensure that we free any previous unfetched results */ -#ifndef PDO_USE_MYSQLND - if (S->stmt) { - if (S->result) { - stmt->column_count = (int)mysql_num_fields(S->result); - } - mysql_stmt_free_result(S->stmt); - } -#endif - if (S->result) { - mysql_free_result(S->result); - S->result = NULL; - } - - if (!mysql_more_results(H->server)) { - /* No more results */ - PDO_DBG_RETURN(0); - } -#if PDO_USE_MYSQLND - if (mysql_next_result(H->server) == FAIL) { - pdo_mysql_error_stmt(stmt); - PDO_DBG_RETURN(0); - } else { - PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt)); - } -#else - if (mysql_next_result(H->server) > 0) { - pdo_mysql_error_stmt(stmt); - PDO_DBG_RETURN(0); - } else { PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt)); } -#endif } /* }}} */ @@ -472,7 +387,6 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da strcpy(stmt->error_code, "HY093"); PDO_DBG_RETURN(0); } - S->params_given++; #ifndef PDO_USE_MYSQLND b = &S->params[param->paramno]; @@ -484,7 +398,7 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da PDO_DBG_RETURN(1); case PDO_PARAM_EVT_EXEC_PRE: - if (S->params_given < (unsigned int) S->num_params) { + if (zend_hash_num_elements(stmt->bound_params) < (unsigned int) S->num_params) { /* too few parameter bound */ PDO_DBG_ERR("too few parameters bound"); strcpy(stmt->error_code, "HY093"); @@ -497,7 +411,7 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da parameter = Z_REFVAL(param->parameter); } -#if PDO_USE_MYSQLND +#ifdef PDO_USE_MYSQLND if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || (Z_TYPE_P(parameter) == IS_NULL)) { mysqlnd_stmt_bind_one_param(S->stmt, param->paramno, parameter, MYSQL_TYPE_NULL); PDO_DBG_RETURN(1); @@ -543,7 +457,7 @@ static int pdo_mysql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_da ; } -#if PDO_USE_MYSQLND +#ifdef PDO_USE_MYSQLND /* Is it really correct to check the zval's type? - But well, that's what the old code below does, too */ PDO_DBG_INF_FMT("param->parameter->type=%d", Z_TYPE(param->parameter)); if (!Z_ISREF(param->parameter)) { @@ -745,7 +659,7 @@ static int pdo_mysql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, size_ /* error invalid column */ PDO_DBG_RETURN(0); } -#if PDO_USE_MYSQLND +#ifdef PDO_USE_MYSQLND if (S->stmt) { Z_TRY_ADDREF(S->stmt->data->result_bind[colno].zv); *ptr = (char*)&S->stmt->data->result_bind[colno].zv; @@ -901,20 +815,18 @@ static int pdo_mysql_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */ PDO_DBG_ENTER("pdo_mysql_stmt_cursor_closer"); PDO_DBG_INF_FMT("stmt=%p", S->stmt); - if (S->result) { - mysql_free_result(S->result); - S->result = NULL; - } + + S->done = 1; + pdo_mysql_free_result(S); if (S->stmt) { - int retval; - retval = mysql_stmt_free_result(S->stmt); - PDO_DBG_RETURN(retval ? 0 : 1); + mysql_stmt_free_result(S->stmt); } while (mysql_more_results(S->H->server)) { MYSQL_RES *res; if (mysql_next_result(S->H->server) != 0) { - break; + pdo_mysql_error_stmt(stmt); + PDO_DBG_RETURN(0); } res = mysql_store_result(S->H->server); if (res) { |