Commit dc3cfb2f authored by Sergei Morozov's avatar Sergei Morozov Committed by Marco Pivetta

[DBAL-2546] Bind result variables after each execution in order to prevent string truncation

parent 7cd0589f
...@@ -168,9 +168,6 @@ class MysqliStatement implements \IteratorAggregate, Statement ...@@ -168,9 +168,6 @@ class MysqliStatement implements \IteratorAggregate, Statement
if (null === $this->_columnNames) { if (null === $this->_columnNames) {
$meta = $this->_stmt->result_metadata(); $meta = $this->_stmt->result_metadata();
if (false !== $meta) { if (false !== $meta) {
// We have a result.
$this->_stmt->store_result();
$columnNames = array(); $columnNames = array();
foreach ($meta->fetch_fields() as $col) { foreach ($meta->fetch_fields() as $col) {
$columnNames[] = $col->name; $columnNames[] = $col->name;
...@@ -178,21 +175,40 @@ class MysqliStatement implements \IteratorAggregate, Statement ...@@ -178,21 +175,40 @@ class MysqliStatement implements \IteratorAggregate, Statement
$meta->free(); $meta->free();
$this->_columnNames = $columnNames; $this->_columnNames = $columnNames;
$this->_rowBindedValues = array_fill(0, count($columnNames), null);
$refs = array();
foreach ($this->_rowBindedValues as $key => &$value) {
$refs[$key] =& $value;
}
if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
} else { } else {
$this->_columnNames = false; $this->_columnNames = false;
} }
} }
if (false !== $this->_columnNames) {
// Store result of every execution which has it. Otherwise it will be impossible
// to execute a new statement in case if the previous one has non-fetched rows
// @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
$this->_stmt->store_result();
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
// it will have to allocate as much memory as it may be needed for the given column type
// (e.g. for a LONGBLOB field it's 4 gigabytes)
// @link https://bugs.php.net/bug.php?id=51386#1270673122
//
// Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been
// previously called on the statement, the values are unbound making the statement unusable.
//
// It's also important that row values are bound after _each_ call to store_result(). Otherwise,
// if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
// to the length of the ones fetched during the previous execution.
$this->_rowBindedValues = array_fill(0, count($this->_columnNames), null);
$refs = array();
foreach ($this->_rowBindedValues as $key => &$value) {
$refs[$key] =& $value;
}
if (!call_user_func_array(array($this->_stmt, 'bind_result'), $refs)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
}
return true; return true;
} }
......
...@@ -53,4 +53,37 @@ class StatementTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -53,4 +53,37 @@ class StatementTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertFalse($value); $this->assertFalse($value);
} }
public function testReuseStatementWithLongerResults()
{
$sm = $this->_conn->getSchemaManager();
$table = new Table('stmt_test_longer_results');
$table->addColumn('param', 'string');
$table->addColumn('val', 'text');
$sm->createTable($table);
$row1 = array(
'param' => 'param1',
'val' => 'X',
);
$this->_conn->insert('stmt_test_longer_results', $row1);
$stmt = $this->_conn->prepare('SELECT param, val FROM stmt_test_longer_results ORDER BY param');
$stmt->execute();
$this->assertArraySubset(array(
array('param1', 'X'),
), $stmt->fetchAll(\PDO::FETCH_NUM));
$row2 = array(
'param' => 'param2',
'val' => 'A bit longer value',
);
$this->_conn->insert('stmt_test_longer_results', $row2);
$stmt->execute();
$this->assertArraySubset(array(
array('param1', 'X'),
array('param2', 'A bit longer value'),
), $stmt->fetchAll(\PDO::FETCH_NUM));
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment