Skip to content

Commit

Permalink
Introduce UnknownAffectedRowsError
Browse files Browse the repository at this point in the history
  • Loading branch information
phansys committed Mar 20, 2023
1 parent 4941fda commit 71e0a46
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 25 deletions.
1 change: 1 addition & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<exclude-pattern>src/Connection.php</exclude-pattern>
<exclude-pattern>src/Schema/Comparator.php</exclude-pattern>
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>tests/Functional/Driver/Mysqli/ResultTest.php</exclude-pattern>
</rule>

<!-- some statement classes close cursor using an empty while-loop -->
Expand Down
6 changes: 6 additions & 0 deletions src/Driver/Mysqli/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -52,6 +53,7 @@ public function quote(string $value): string
return "'" . $this->connection->escape_string($value) . "'";
}

/** @return int<0, max>|numeric-string */
public function exec(string $sql): int|string
{
try {
Expand All @@ -64,6 +66,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;
}

Expand Down
31 changes: 31 additions & 0 deletions src/Driver/Mysqli/Exception/UnknownAffectedRowsError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Driver\Mysqli\Exception;

use Doctrine\DBAL\Driver\AbstractException;
use mysqli;
use mysqli_sql_exception;
use mysqli_stmt;
use ReflectionProperty;

/**
* @internal
*
* @psalm-immutable
*/
final class UnknownAffectedRowsError extends AbstractException
{
public static function new(mysqli|mysqli_stmt $executedResource): self
{
return new self($executedResource->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);
}
}
3 changes: 2 additions & 1 deletion src/Driver/Mysqli/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
59 changes: 35 additions & 24 deletions tests/Functional/Driver/Mysqli/ResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,70 @@

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 mysqli_stmt;

use function sprintf;

use const MYSQLI_REPORT_OFF;

/** @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;
if (! TestUtil::isDriverOneOf('mysqli')) {
self::markTestSkipped('This test requires the mysqli driver.');
}

$this->connection->executeQuery('CREATE TABLE my_table (my_col_1 INT NOT NULL);');
$nativeConnection = $this->connection->getNativeConnection();

return;
}
self::assertInstanceOf(mysqli::class, $nativeConnection);

self::markTestSkipped('This test requires the mysqli driver.');
$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(sprintf('INSERT INTO %s VALUES(7);', self::TABLE_NAME));

$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);');
// Ensure report mode configuration is "off" (like the default value for PHP < 8.1).
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = MYSQLI_REPORT_OFF;

$mysqliStmt = $this->nativeConnection
->prepare(sprintf('INSERT INTO %s VALUES (NULL);', self::TABLE_NAME));

self::assertInstanceOf(mysqli_stmt::class, $mysqliStmt);

self::assertSame(-1, $mysqliStmt->affected_rows);
$mysqliStmt->execute();

$this->expectException(ConnectionError::class);
$this->expectException(UnknownAffectedRowsError::class);

(new Result($mysqliStmt))->rowCount();
}
Expand Down

0 comments on commit 71e0a46

Please sign in to comment.