Handle binding errors in OCI8Statement::execute() and MySQLiStatement::execute()

parent 4e63a872
# Upgrade to 3.0
## BC BREAK `Statement::execute()` with redundant parameters.
Similarly to the drivers based on `pdo_pgsql` and `pdo_sqlsrv`, `OCI8Statement::execute()` and `MySQLiStatement::execute()` do not longer ignore redundant parameters.
## BC BREAK: `Doctrine\DBAL\Types\Type::getDefaultLength()` removed
The `Doctrine\DBAL\Types\Type::getDefaultLength()` method has been removed as it served no purpose.
......
......@@ -48,7 +48,7 @@ class MysqliStatement implements IteratorAggregate, Statement
protected $_rowBindedValues = [];
/** @var mixed[] */
protected $_bindedValues;
protected $_bindedValues = [];
/** @var string */
protected $types;
......@@ -136,15 +136,13 @@ class MysqliStatement implements IteratorAggregate, Statement
*/
public function execute($params = null)
{
if ($this->_bindedValues !== null) {
if ($params !== null) {
if ($params !== null && count($params) > 0) {
if (! $this->bindUntypedValues($params)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->errno);
}
} else {
$this->bindTypedParameters();
}
}
if (! $this->_stmt->execute()) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
......@@ -232,7 +230,7 @@ class MysqliStatement implements IteratorAggregate, Statement
$values[$parameter] = $value;
}
if (! $this->_stmt->bind_param($types, ...$values)) {
if (count($values) > 0 && ! $this->_stmt->bind_param($types, ...$values)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
......
......@@ -380,9 +380,13 @@ class OCI8Statement implements IteratorAggregate, Statement
foreach ($params as $key => $val) {
if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key + 1, $val);
$param = $key + 1;
} else {
$this->bindValue($key, $val);
$param = $key;
}
if (! $this->bindValue($param, $val)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
}
}
......
......@@ -40,24 +40,15 @@ class OCI8StatementTest extends DbalTestCase
->disableOriginalConstructor()
->getMock();
$statement->expects($this->at(0))
foreach ($params as $index => $value) {
$statement->expects($this->at($index))
->method('bindValue')
->with(
$this->equalTo(1),
$this->equalTo($params[0])
);
$statement->expects($this->at(1))
->method('bindValue')
->with(
$this->equalTo(2),
$this->equalTo($params[1])
);
$statement->expects($this->at(2))
->method('bindValue')
->with(
$this->equalTo(3),
$this->equalTo($params[2])
);
$this->equalTo($index + 1),
$this->equalTo($value)
)
->willReturn(true);
}
// the return value is irrelevant to the test
// but it has to be compatible with the method signature
......
......@@ -2,6 +2,7 @@
namespace Doctrine\Tests\DBAL\Functional;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOracleDriver;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\FetchMode;
......@@ -10,6 +11,7 @@ use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
use Doctrine\Tests\DbalFunctionalTestCase;
use function base64_decode;
use function sprintf;
use function stream_get_contents;
class StatementTest extends DbalFunctionalTestCase
......@@ -319,4 +321,40 @@ EOF
self::assertEquals(1, $result);
}
public function testExecWithRedundantParameters() : void
{
$driver = $this->connection->getDriver()->getName();
switch ($driver) {
case 'pdo_mysql':
case 'pdo_oracle':
case 'pdo_sqlsrv':
self::markTestSkipped(sprintf(
'PDOStatement::execute() implemented in the "%s" driver does not report redundant parameters',
$driver
));
return;
case 'ibm_db2':
self::markTestSkipped('db2_execute() does not report redundant parameters');
return;
case 'sqlsrv':
self::markTestSkipped('sqlsrv_prepare() does not report redundant parameters');
return;
}
$platform = $this->connection->getDatabasePlatform();
$query = $platform->getDummySelectSQL();
$stmt = $this->connection->prepare($query);
// we want to make sure the exception is thrown by the DBAL code, not by PHPUnit due to a PHP-level error,
// but the wrapper connection wraps everything in a DBAL exception
$this->iniSet('error_reporting', 0);
self::expectException(DBALException::class);
$stmt->execute([null]);
}
}
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