diff --git a/src/Driver/PgSQL/Result.php b/src/Driver/PgSQL/Result.php index 2ddbc343633..18494bd05fe 100644 --- a/src/Driver/PgSQL/Result.php +++ b/src/Driver/PgSQL/Result.php @@ -51,6 +51,15 @@ public function __construct($result) $this->result = $result; } + public function __destruct() + { + if (! isset($this->result)) { + return; + } + + $this->free(); + } + /** {@inheritdoc} */ public function fetchNumeric() { diff --git a/tests/Functional/Driver/PgSQL/ResultTest.php b/tests/Functional/Driver/PgSQL/ResultTest.php index 4526dbb7c7c..f7f1504ad70 100644 --- a/tests/Functional/Driver/PgSQL/ResultTest.php +++ b/tests/Functional/Driver/PgSQL/ResultTest.php @@ -4,12 +4,27 @@ namespace Doctrine\DBAL\Tests\Functional\Driver\PgSQL; +use Doctrine\DBAL\Driver\PgSQL\Result; use Doctrine\DBAL\Tests\FunctionalTestCase; use Doctrine\DBAL\Tests\TestUtil; use Doctrine\DBAL\Types\Types; +use Error; +use ErrorException; use Generator; +use PgSql\Connection as PgSqlConnection; +use function array_slice; +use function assert; use function chr; +use function func_get_args; +use function is_resource; +use function pg_query; +use function pg_result_status; +use function restore_error_handler; +use function set_error_handler; + +use const PGSQL_TUPLES_OK; +use const PHP_VERSION_ID; class ResultTest extends FunctionalTestCase { @@ -222,4 +237,34 @@ public function testTypeConversionWithDuplicateFieldNames(): void $this->connection->fetchFirstColumn('SELECT a.*, b.* FROM types_test a, types_test2 b'), ); } + + public function testResultIsFreedOnDestruct(): void + { + $pgsqlConnection = $this->connection->getNativeConnection(); + assert($pgsqlConnection instanceof PgSqlConnection || is_resource($pgsqlConnection)); + $pgsqlResult = pg_query($pgsqlConnection, 'SELECT 1'); + assert($pgsqlResult !== false); + + self::assertSame(PGSQL_TUPLES_OK, pg_result_status($pgsqlResult)); + + new Result($pgsqlResult); + + set_error_handler(static function (int $severity, string $message): void { + throw new ErrorException($message, 0, $severity, ...array_slice(func_get_args(), 2, 2)); + }); + + try { + if (PHP_VERSION_ID >= 80100) { + $this->expectException(Error::class); + $this->expectExceptionMessage('PostgreSQL result has already been closed'); + } else { + $this->expectException(ErrorException::class); + $this->expectExceptionMessage('supplied resource is not a valid PostgreSQL result resource'); + } + + pg_result_status($pgsqlResult); + } finally { + restore_error_handler(); + } + } }