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 @@ ...@@ -3,6 +3,9 @@
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Connection; 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\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
use stdClass; use stdClass;
...@@ -21,7 +24,7 @@ use function db2_pconnect; ...@@ -21,7 +24,7 @@ use function db2_pconnect;
use function db2_prepare; use function db2_prepare;
use function db2_rollback; use function db2_rollback;
use function db2_server_info; use function db2_server_info;
use function db2_stmt_errormsg; use function error_get_last;
use function func_get_args; use function func_get_args;
use function is_bool; use function is_bool;
...@@ -52,7 +55,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -52,7 +55,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
} }
if ($conn === false) { if ($conn === false) {
throw new DB2Exception(db2_conn_errormsg()); throw ConnectionFailed::new();
} }
$this->conn = $conn; $this->conn = $conn;
...@@ -83,8 +86,9 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -83,8 +86,9 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function prepare($sql) public function prepare($sql)
{ {
$stmt = @db2_prepare($this->conn, $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); return new DB2Statement($stmt);
...@@ -125,7 +129,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -125,7 +129,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
$stmt = @db2_exec($this->conn, $statement); $stmt = @db2_exec($this->conn, $statement);
if ($stmt === false) { if ($stmt === false) {
throw new DB2Exception(db2_stmt_errormsg()); throw ConnectionError::new($this->conn);
} }
return db2_num_rows($stmt); return db2_num_rows($stmt);
...@@ -156,7 +160,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -156,7 +160,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function commit() public function commit()
{ {
if (! db2_commit($this->conn)) { 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); $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
...@@ -171,7 +175,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection ...@@ -171,7 +175,7 @@ class DB2Connection implements Connection, ServerInfoAwareConnection
public function rollBack() public function rollBack()
{ {
if (! db2_rollback($this->conn)) { 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); $result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Exception; use Doctrine\DBAL\Driver\AbstractDriverException;
/** /**
* @psalm-immutable * @psalm-immutable
*/ */
class DB2Exception extends Exception class DB2Exception extends AbstractDriverException
{ {
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils; use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator; use Doctrine\DBAL\Driver\StatementIterator;
...@@ -141,7 +142,7 @@ class DB2Statement implements IteratorAggregate, Statement, Result ...@@ -141,7 +142,7 @@ class DB2Statement implements IteratorAggregate, Statement, Result
$this->bindParam[$position] =& $variable; $this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) { 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 ...@@ -228,7 +229,7 @@ class DB2Statement implements IteratorAggregate, Statement, Result
$this->lobs = []; $this->lobs = [];
if ($retval === false) { if ($retval === false) {
throw new DB2Exception(db2_stmt_errormsg()); throw StatementError::new($this->stmt);
} }
$this->result = true; $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 @@ ...@@ -99,6 +99,11 @@
<exclude-pattern>lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern> <exclude-pattern>lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern>
</rule> </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 --> <!-- see https://github.com/doctrine/dbal/issues/3377 -->
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator"> <rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
<exclude-pattern>lib/Doctrine/DBAL/Schema/Comparator.php</exclude-pattern> <exclude-pattern>lib/Doctrine/DBAL/Schema/Comparator.php</exclude-pattern>
......
...@@ -5,6 +5,7 @@ namespace Doctrine\Tests\DBAL\Functional; ...@@ -5,6 +5,7 @@ namespace Doctrine\Tests\DBAL\Functional;
use DateTime; use DateTime;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver; use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver; use Doctrine\DBAL\Driver\OCI8\Driver as Oci8Driver;
use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\Driver\PDOConnection;
...@@ -245,8 +246,9 @@ class DataAccessTest extends DbalFunctionalTestCase ...@@ -245,8 +246,9 @@ class DataAccessTest extends DbalFunctionalTestCase
/** /**
* @group DBAL-209 * @group DBAL-209
* @dataProvider fetchProvider
*/ */
public function testFetchAllWithMissingTypes(): void public function testFetchAllWithMissingTypes(callable $fetch): void
{ {
if ( if (
$this->connection->getDriver() instanceof MySQLiDriver || $this->connection->getDriver() instanceof MySQLiDriver ||
...@@ -255,13 +257,51 @@ class DataAccessTest extends DbalFunctionalTestCase ...@@ -255,13 +257,51 @@ class DataAccessTest extends DbalFunctionalTestCase
$this->markTestSkipped('mysqli and sqlsrv actually supports this'); $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'; $datetimeString = '2010-01-01 10:10:10';
$datetime = new DateTime($datetimeString); $datetime = new DateTime($datetimeString);
$sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?'; $sql = 'SELECT test_int, test_datetime FROM fetch_table WHERE test_int = ? AND test_datetime = ?';
$this->expectException(DBALException::class); $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 public function testFetchBoth(): void
...@@ -319,24 +359,6 @@ class DataAccessTest extends DbalFunctionalTestCase ...@@ -319,24 +359,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $row['test_datetime']); 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 public function testFetchArray(): void
{ {
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?'; $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
...@@ -366,24 +388,6 @@ class DataAccessTest extends DbalFunctionalTestCase ...@@ -366,24 +388,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $row[1]); 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 public function testFetchColumn(): void
{ {
$sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?'; $sql = 'SELECT test_int, test_string FROM fetch_table WHERE test_int = ? AND test_string = ?';
...@@ -415,24 +419,6 @@ class DataAccessTest extends DbalFunctionalTestCase ...@@ -415,24 +419,6 @@ class DataAccessTest extends DbalFunctionalTestCase
self::assertStringStartsWith($datetimeString, $column); 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 * @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); ...@@ -5,10 +5,15 @@ declare(strict_types=1);
namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2; namespace Doctrine\Tests\DBAL\Functional\Driver\IBMDB2;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\Tests\DbalFunctionalTestCase; use Doctrine\Tests\DbalFunctionalTestCase;
use function extension_loaded; use function extension_loaded;
use const E_ALL;
use const E_NOTICE;
use const E_WARNING;
class DB2StatementTest extends DbalFunctionalTestCase class DB2StatementTest extends DbalFunctionalTestCase
{ {
protected function setUp(): void protected function setUp(): void
...@@ -28,12 +33,14 @@ class DB2StatementTest extends DbalFunctionalTestCase ...@@ -28,12 +33,14 @@ class DB2StatementTest extends DbalFunctionalTestCase
public function testExecutionErrorsAreNotSuppressed(): void 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 // prevent the PHPUnit error handler from handling the errors that db2_execute() may trigger
$wrappedStmt = $stmt->getWrappedStatement(); $this->iniSet('error_reporting', (string) (E_ALL & ~E_WARNING & ~E_NOTICE));
$this->expectNotice(); $this->expectException(StatementError::class);
$wrappedStmt->execute([[]]); $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