Unverified Commit e1d5934e authored by Sergei Morozov's avatar Sergei Morozov Committed by GitHub

Merge pull request #4085 from morozov/ibm-db2-driver-exception

The IBM DB2 driver Exception class must implement the DriverException interface
parents 2cfd54ae e676583f
......@@ -3,6 +3,9 @@
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use stdClass;
......@@ -21,7 +24,7 @@ use function db2_pconnect;
use function db2_prepare;
use function db2_rollback;
use function db2_server_info;
use function db2_stmt_errormsg;
use function error_get_last;
use function func_get_args;
use function is_bool;
......@@ -52,7 +55,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
}
if ($conn === false) {
throw new DB2Exception(db2_conn_errormsg());
throw ConnectionFailed::new();
}
$this->conn = $conn;
......@@ -83,8 +86,9 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function prepare($sql)
{
$stmt = @db2_prepare($this->conn, $sql);
if (! $stmt) {
throw new DB2Exception(db2_stmt_errormsg());
if ($stmt === false) {
throw PrepareFailed::new(error_get_last()['message']);
}
return new DB2Statement($stmt);
......@@ -125,7 +129,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
$stmt = @db2_exec($this->conn, $statement);
if ($stmt === false) {
throw new DB2Exception(db2_stmt_errormsg());
throw ConnectionError::new($this->conn);
}
return db2_num_rows($stmt);
......@@ -156,7 +160,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function commit()
{
if (! db2_commit($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
throw ConnectionError::new($this->conn);
}
$result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
......@@ -171,7 +175,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function rollBack()
{
if (! db2_rollback($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
throw ConnectionError::new($this->conn);
}
$result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
......
......@@ -2,11 +2,11 @@
namespace Doctrine\DBAL\Driver\IBMDB2;
use Exception;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @psalm-immutable
*/
class DB2Exception extends Exception
class DB2Exception extends AbstractDriverException
{
}
......@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
......@@ -141,7 +142,7 @@ class DB2Statement implements IteratorAggregate, Statement, Result
$this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw new DB2Exception(db2_stmt_errormsg());
throw StatementError::new($this->stmt);
}
}
......@@ -228,7 +229,7 @@ class DB2Statement implements IteratorAggregate, Statement, Result
$this->lobs = [];
if ($retval === false) {
throw new DB2Exception(db2_stmt_errormsg());
throw StatementError::new($this->stmt);
}
$this->result = true;
......
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @psalm-immutable
*/
final class ConnectionError extends DB2Exception
{
/**
* @param resource $connection
*/
public static function new($connection): self
{
return new self(db2_conn_errormsg($connection), db2_conn_error($connection));
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @psalm-immutable
*/
final class ConnectionFailed extends DB2Exception
{
public static function new(): self
{
return new self(db2_conn_errormsg(), db2_conn_error());
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
/**
* @psalm-immutable
*/
final class PrepareFailed extends DB2Exception
{
public static function new(string $message): self
{
return new self($message);
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @psalm-immutable
*/
final class StatementError extends DB2Exception
{
/**
* @param resource $statement
*/
public static function new($statement): self
{
return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement));
}
}
......@@ -99,6 +99,11 @@
<exclude-pattern>lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern>
</rule>
<!-- See https://github.com/slevomat/coding-standard/issues/1038 -->
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
<exclude-pattern>tests/Doctrine/Tests/DBAL/Functional/Driver/IBMDB2/DB2StatementTest.php</exclude-pattern>
</rule>
<!-- see https://github.com/doctrine/dbal/issues/3377 -->
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
<exclude-pattern>lib/Doctrine/DBAL/Schema/Comparator.php</exclude-pattern>
......
......@@ -5,6 +5,7 @@ namespace Doctrine\Tests\DBAL\Functional;
use DateTime;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
use Doctrine\DBAL\Driver\PDOConnection;
......@@ -245,8 +246,9 @@ class DataAccessTest extends DbalFunctionalTestCase
/**
* @group DBAL-209
* @dataProvider fetchProvider
*/
public function testFetchAllWithMissingTypes(): void
public function testFetchAllWithMissingTypes(callable $fetch): void
{
if (
$this->connection->getDriver() instanceof MySQLiDriver ||
......@@ -255,13 +257,51 @@ class DataAccessTest extends DbalFunctionalTestCase
$this->markTestSkipped('mysqli and sqlsrv actually supports this');
}
if (
$this->connection->getDriver() instanceof DB2Driver
) {
$this->markTestSkipped(
'ibm_ibm2 may or may not report the error depending on the PHP version and the connection state'
);
}
$datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$this->expectException(DBALException::class);
$this->connection->fetchAll($sql, [1, $datetime]);
$fetch($this->connection, $sql, [1, $datetime]);
}
/**
* @return iterable<string,array{0:callable}>
*/
public static function fetchProvider(): iterable
{
yield 'fetch-all-associative' => [
static function (Connection $connection, string $query, array $params): void {
$connection->fetchAll($query, $params);
},
];
yield 'fetch-numeric' => [
static function (Connection $connection, string $query, array $params): void {
$connection->fetchArray($query, $params);
},
];
yield 'fetch-associative' => [
static function (Connection $connection, string $query, array $params): void {
$connection->fetchAssoc($query, $params);
},
];
yield 'fetch-one' => [
static function (Connection $connection, string $query, array $params): void {
$connection->fetchColumn($query, $params);
},
];
}
public function testFetchBoth(): void
......@@ -319,24 +359,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $row['test_datetime']);
}
public function testFetchAssocWithMissingTypes(): void
{
if (
$this->connection->getDriver() instanceof MySQLiDriver ||
$this->connection->getDriver() instanceof SQLSrvDriver
) {
$this->markTestSkipped('mysqli and sqlsrv actually supports this');
}
$datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$this->expectException(DBALException::class);
$this->connection->fetchAssoc($sql, [1, $datetime]);
}
public function testFetchArray(): void
{
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
......@@ -366,24 +388,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $row[1]);
}
public function testFetchArrayWithMissingTypes(): void
{
if (
$this->connection->getDriver() instanceof MySQLiDriver ||
$this->connection->getDriver() instanceof SQLSrvDriver
) {
$this->markTestSkipped('mysqli and sqlsrv actually supports this');
}
$datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$this->expectException(DBALException::class);
$this->connection->fetchArray($sql, [1, $datetime]);
}
public function testFetchColumn(): void
{
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
......@@ -415,24 +419,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $column);
}
public function testFetchColumnWithMissingTypes(): void
{
if (
$this->connection->getDriver() instanceof MySQLiDriver ||
$this->connection->getDriver() instanceof SQLSrvDriver
) {
$this->markTestSkipped('mysqli and sqlsrv actually supports this');
}
$datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$this->expectException(DBALException::class);
$this->connection->fetchColumn($sql, [1, $datetime], 1);
}
/**
* @group DDC-697
*/
......
<?php
namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\DB2Connection;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\Tests\DbalFunctionalTestCase;
use ReflectionProperty;
use function db2_close;
use function extension_loaded;
class ConnectionTest extends DbalFunctionalTestCase
{
protected function setUp(): void
{
if (! extension_loaded('ibm_db2')) {
$this->markTestSkipped('ibm_db2 is not installed.');
}
parent::setUp();
if ($this->connection->getDriver() instanceof DB2Driver) {
return;
}
$this->markTestSkipped('ibm_db2 only test.');
}
protected function tearDown(): void
{
$this->resetSharedConn();
}
public function testConnectionFailure(): void
{
$this->expectException(ConnectionFailed::class);
new DB2Connection(['dbname' => 'garbage'], '', '');
}
public function testPrepareFailure(): void
{
$driverConnection = $this->connection->getWrappedConnection();
$re = new ReflectionProperty($driverConnection, 'conn');
$re->setAccessible(true);
$conn = $re->getValue($driverConnection);
db2_close($conn);
$this->expectException(PrepareFailed::class);
$driverConnection->prepare('SELECT 1');
}
}
......@@ -5,10 +5,15 @@ declare(strict_types=1);
namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\Tests\DbalFunctionalTestCase;
use function extension_loaded;
use const E_ALL;
use const E_NOTICE;
use const E_WARNING;
class DB2StatementTest extends DbalFunctionalTestCase
{
protected function setUp(): void
......@@ -28,12 +33,14 @@ class DB2StatementTest extends DbalFunctionalTestCase
public function testExecutionErrorsAreNotSuppressed(): void
{
$stmt = $this->connection->prepare('SELECT * FROM SYSIBM.SYSDUMMY1 WHERE \'foo\' = ?');
$driverConnection = $this->connection->getWrappedConnection();
$stmt = $driverConnection->prepare('SELECT * FROM SYSIBM.SYSDUMMY1 WHERE \'foo\' = ?');
// unwrap the statement to prevent the wrapper from handling the PHPUnit-originated exception
$wrappedStmt = $stmt->getWrappedStatement();
// prevent the PHPUnit error handler from handling the errors that db2_execute() may trigger
$this->iniSet('error_reporting', (string) (E_ALL & ~E_WARNING & ~E_NOTICE));
$this->expectNotice();
$wrappedStmt->execute([[]]);
$this->expectException(StatementError::class);
$stmt->execute([[]]);
}
}
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