From 8cf7c687f5dd53aebaed8cac36f95c5110117d64 Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Mon, 20 Mar 2023 19:12:38 -0300 Subject: [PATCH] Introduce `UnknownAffectedRowsError` --- src/Driver/Mysqli/Connection.php | 5 ++ .../Exception/UnknownAffectedRowsError.php | 31 +++++++++++ src/Driver/Mysqli/Result.php | 3 +- tests/Functional/Driver/Mysqli/ResultTest.php | 53 +++++++++++-------- 4 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 src/Driver/Mysqli/Exception/UnknownAffectedRowsError.php diff --git a/src/Driver/Mysqli/Connection.php b/src/Driver/Mysqli/Connection.php index cc00fb62c1d..5ca50e606c0 100644 --- a/src/Driver/Mysqli/Connection.php +++ b/src/Driver/Mysqli/Connection.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\Connection as ConnectionInterface; use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; +use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownAffectedRowsError; use mysqli; use mysqli_sql_exception; @@ -64,6 +65,10 @@ public function exec(string $sql): int|string throw ConnectionError::new($this->connection); } + if (0 > $this->connection->affected_rows) { + throw UnknownAffectedRowsError::new($this->connection); + } + return $this->connection->affected_rows; } diff --git a/src/Driver/Mysqli/Exception/UnknownAffectedRowsError.php b/src/Driver/Mysqli/Exception/UnknownAffectedRowsError.php new file mode 100644 index 00000000000..00fa7c4f402 --- /dev/null +++ b/src/Driver/Mysqli/Exception/UnknownAffectedRowsError.php @@ -0,0 +1,31 @@ +error, $executedResource->sqlstate, $executedResource->errno); + } + + public static function upcast(mysqli_sql_exception $exception): self + { + $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); + + return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); + } +} diff --git a/src/Driver/Mysqli/Result.php b/src/Driver/Mysqli/Result.php index ee3967e7e4f..449cbeeac19 100644 --- a/src/Driver/Mysqli/Result.php +++ b/src/Driver/Mysqli/Result.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\Exception; use Doctrine\DBAL\Driver\FetchUtils; use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; +use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownAffectedRowsError; use Doctrine\DBAL\Driver\Result as ResultInterface; use mysqli_sql_exception; use mysqli_stmt; @@ -150,7 +151,7 @@ public function rowCount(): int|string } if (0 > $this->statement->affected_rows) { - throw StatementError::new($this->statement); + throw UnknownAffectedRowsError::new($this->statement); } return $this->statement->affected_rows; diff --git a/tests/Functional/Driver/Mysqli/ResultTest.php b/tests/Functional/Driver/Mysqli/ResultTest.php index f216765a15d..ab2a41c6a15 100644 --- a/tests/Functional/Driver/Mysqli/ResultTest.php +++ b/tests/Functional/Driver/Mysqli/ResultTest.php @@ -4,59 +4,68 @@ namespace Doctrine\DBAL\Tests\Functional\Driver\Mysqli; -use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError; -use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError; +use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownAffectedRowsError; use Doctrine\DBAL\Driver\Mysqli\Result; -use Doctrine\DBAL\Exception\DriverException; +use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Tests\FunctionalTestCase; use Doctrine\DBAL\Tests\TestUtil; +use mysqli; use mysqli_driver; +use function sprintf; + /** @requires extension mysqli */ class ResultTest extends FunctionalTestCase { + private const TABLE_NAME = 'tesult_test_table'; + + private mysqli $nativeConnection; + protected function setUp(): void { - if (TestUtil::isDriverOneOf('mysqli')) { - $mysqliDriver = new mysqli_driver(); - $mysqliDriver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT; - - $this->connection->executeQuery('CREATE TABLE my_table (my_col_1 INT NOT NULL);'); + if (! TestUtil::isDriverOneOf('mysqli')) { + self::markTestSkipped('This test requires the mysqli driver.'); return; } - self::markTestSkipped('This test requires the mysqli driver.'); + $nativeConnection = $this->connection->getNativeConnection(); + + self::assertInstanceOf(mysqli::class, $nativeConnection); + + $this->nativeConnection = $nativeConnection; + + $table = new Table(self::TABLE_NAME); + $table->addColumn('my_col_1', 'integer', ['notnull' => true]); + + $this->dropAndCreateTable($table); } protected function tearDown(): void { - $this->connection->executeStatement('DROP TABLE IF EXISTS my_table;'); + $this->dropTableIfExists(self::TABLE_NAME); } public function testSuccessfulRowCountFromAffectedRows(): void { - // var_dump($this->connection->getNativeConnection()->error); - // var_dump($this->connection->getNativeConnection()->error_list); - // var_dump($this->connection->getNativeConnection()->info); - // self::assertSame(0, $this->connection->getNativeConnection()->affected_rows); - - self::assertSame(0, $this->connection->getNativeConnection()->affected_rows); - $result = $this->connection->executeQuery('INSERT INTO my_table VALUES(7);'); - self::assertSame(1, $this->connection->getNativeConnection()->affected_rows); self::assertSame(1, $result->rowCount()); } public function testFailingRowCountFromAffectedRows(): void { - $mysqliStmt = $this->connection->getNativeConnection() - ->prepare('INSERT INTO my_table VALUES(7);'); + $mysqliStmt = $this->nativeConnection + ->prepare(sprintf('INSERT INTO %s VALUES (NULL);', self::TABLE_NAME)); + + $mysqliDriver = new mysqli_driver(); + + // Ensure report mode configuration is "off" (like the default value for PHP < 8.1). + $mysqliDriver->report_mode = MYSQLI_REPORT_OFF; - self::assertSame(-1, $mysqliStmt->affected_rows); + $mysqliStmt->execute(); - $this->expectException(ConnectionError::class); + $this->expectException(UnknownAffectedRowsError::class); (new Result($mysqliStmt))->rowCount(); }