mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Handle changing column count in mysqlnd result binding
If the count changes from prepare to execute and result_bind is alreadly allocated, reallocate it there. This is something of a hack. It would be cleaner to require that result bindings are registered only after execute, when the final result set fields are known. But mysqli at least directly exposes this to the user, so we have no guarantee.
This commit is contained in:
parent
2df09b9b64
commit
311a77d08e
2 changed files with 25 additions and 50 deletions
|
@ -36,7 +36,6 @@ enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, z
|
|||
enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer);
|
||||
|
||||
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt);
|
||||
static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, const unsigned int param_no);
|
||||
|
||||
/* {{{ mysqlnd_stmt::store_result */
|
||||
static MYSQLND_RES *
|
||||
|
@ -542,7 +541,27 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
|
|||
stmt->result->conn = conn->m->get_reference(conn);
|
||||
}
|
||||
|
||||
/* Update stmt->field_count as SHOW sets it to 0 at prepare */
|
||||
/* If the field count changed, update the result_bind structure. Ideally result_bind
|
||||
* would only ever be created after execute, in which case the size cannot change anymore,
|
||||
* but at least in mysqli this does not seem enforceable. */
|
||||
if (stmt->result_bind && conn->field_count != stmt->field_count) {
|
||||
if (conn->field_count < stmt->field_count) {
|
||||
/* Number of columns decreased, free bindings. */
|
||||
for (unsigned i = conn->field_count; i < stmt->field_count; i++) {
|
||||
zval_ptr_dtor(&stmt->result_bind[i].zv);
|
||||
}
|
||||
}
|
||||
stmt->result_bind =
|
||||
mnd_erealloc(stmt->result_bind, conn->field_count * sizeof(MYSQLND_RESULT_BIND));
|
||||
if (conn->field_count > stmt->field_count) {
|
||||
/* Number of columns increase, initialize new ones. */
|
||||
for (unsigned i = stmt->field_count; i < conn->field_count; i++) {
|
||||
ZVAL_UNDEF(&stmt->result_bind[i].zv);
|
||||
stmt->result_bind[i].bound = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt->field_count = stmt->result->field_count = conn->field_count;
|
||||
if (stmt->result->stored_data) {
|
||||
stmt->result->stored_data->lengths = NULL;
|
||||
|
@ -1577,22 +1596,13 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned i
|
|||
SET_EMPTY_ERROR(conn->error_info);
|
||||
|
||||
if (stmt->field_count) {
|
||||
mysqlnd_stmt_separate_one_result_bind(s, param_no);
|
||||
/* Guaranteed is that stmt->result_bind is NULL */
|
||||
if (!stmt->result_bind) {
|
||||
stmt->result_bind = mnd_ecalloc(stmt->field_count, sizeof(MYSQLND_RESULT_BIND));
|
||||
} else {
|
||||
stmt->result_bind = mnd_erealloc(stmt->result_bind, stmt->field_count * sizeof(MYSQLND_RESULT_BIND));
|
||||
}
|
||||
if (!stmt->result_bind) {
|
||||
DBG_RETURN(FAIL);
|
||||
if (stmt->result_bind[param_no].bound) {
|
||||
zval_ptr_dtor(&stmt->result_bind[param_no].zv);
|
||||
}
|
||||
ZVAL_NULL(&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");
|
||||
|
@ -1968,37 +1978,6 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s)
|
|||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysqlnd_stmt_separate_one_result_bind */
|
||||
static void
|
||||
mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, const unsigned int param_no)
|
||||
{
|
||||
MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
|
||||
DBG_ENTER("mysqlnd_stmt_separate_one_result_bind");
|
||||
if (!stmt) {
|
||||
DBG_VOID_RETURN;
|
||||
}
|
||||
DBG_INF_FMT("stmt=%lu result_bind=%p field_count=%u param_no=%u", 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("%u has refcount=%u", param_no, Z_REFCOUNTED(stmt->result_bind[param_no].zv)? Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0);
|
||||
zval_ptr_dtor(&stmt->result_bind[param_no].zv);
|
||||
}
|
||||
|
||||
DBG_VOID_RETURN;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysqlnd_stmt::free_stmt_result */
|
||||
static void
|
||||
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
--TEST--
|
||||
MySQL Prepared Statements and different column counts
|
||||
--XFAIL--
|
||||
nextRowset() problem with stored proc & emulation mode & mysqlnd
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
|
@ -25,10 +23,8 @@ if ($version < 50000)
|
|||
$db = MySQLPDOTest::factory();
|
||||
|
||||
function check_result($offset, $stmt, $columns) {
|
||||
|
||||
do {
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
} while ($stmt->nextRowSet());
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$stmt->nextRowSet();
|
||||
|
||||
if (!isset($row['one']) || ($row['one'] != 1)) {
|
||||
printf("[%03d + 1] Expecting array('one' => 1), got %s\n", $offset, var_export($row, true));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue