summaryrefslogtreecommitdiff
path: root/ext/pdo_mysql/mysql_statement.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pdo_mysql/mysql_statement.c')
-rw-r--r--ext/pdo_mysql/mysql_statement.c404
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) {