Skip to content

Commit ce220bc

Browse files
committed
Merge branch '4.2.x' into 4.3.x
* 4.2.x: Create stubs instead of mocks (#6564) [Bug] Query Cache mangled if saved by-reference (#6552) test: remove ->expects(self::any()) Fix typo in PostgreSql documentation reference fix Acknowledge the existence of 3.10 (#6553) test: cover nested transactions
2 parents 33f5589 + e729f88 commit ce220bc

File tree

11 files changed

+107
-46
lines changed

11 files changed

+107
-46
lines changed

.doctrine-project.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
"slug": "4.0",
3636
"maintained": false
3737
},
38+
{
39+
"name": "3.10",
40+
"branchName": "3.10.x",
41+
"slug": "3.10",
42+
"upcoming": true
43+
},
3844
{
3945
"name": "3.9",
4046
"branchName": "3.9.x",

README.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Doctrine DBAL
22

3-
| [5.0-dev][5.0] | [4.3-dev][4.3] | [4.2][4.2] | [3.9][3.9] |
4-
|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|
5-
| [![GitHub Actions][GA 5.0 image]][GA 5.0] | [![GitHub Actions][GA 4.3 image]][GA 4.3] | [![GitHub Actions][GA 4.2 image]][GA 4.2] | [![GitHub Actions][GA 3.9 image]][GA 3.9] |
6-
| [![AppVeyor][AppVeyor 5.0 image]][AppVeyor 5.0] | [![AppVeyor][AppVeyor 4.3 image]][AppVeyor 4.3] | [![AppVeyor][AppVeyor 4.2 image]][AppVeyor 4.2] | [![AppVeyor][AppVeyor 3.9 image]][AppVeyor 3.9] |
7-
| [![Code Coverage][Coverage 5.0 image]][CodeCov 5.0] | [![Code Coverage][Coverage 4.3 image]][CodeCov 4.3] | [![Code Coverage][Coverage 4.2 image]][CodeCov 4.2] | [![Code Coverage][Coverage 3.9 image]][CodeCov 3.9] |
8-
| N/A | N/A | [![Type Coverage][TypeCov image]][TypeCov] | N/A |
3+
| [5.0-dev][5.0] | [4.3-dev][4.3] | [4.2][4.2] | [3.10][3.10] | [3.9][3.9] |
4+
|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|:-----------------------------------------------------:|:---------------------------------------------------:|
5+
| [![GitHub Actions][GA 5.0 image]][GA 5.0] | [![GitHub Actions][GA 4.3 image]][GA 4.3] | [![GitHub Actions][GA 4.2 image]][GA 4.2] | [![GitHub Actions][GA 3.10 image]][GA 3.10] | [![GitHub Actions][GA 3.9 image]][GA 3.9] |
6+
| [![AppVeyor][AppVeyor 5.0 image]][AppVeyor 5.0] | [![AppVeyor][AppVeyor 4.3 image]][AppVeyor 4.3] | [![AppVeyor][AppVeyor 4.2 image]][AppVeyor 4.2] | [![AppVeyor][AppVeyor 3.10 image]][AppVeyor 3.10] | [![AppVeyor][AppVeyor 3.9 image]][AppVeyor 3.9] |
7+
| [![Code Coverage][Coverage 5.0 image]][CodeCov 5.0] | [![Code Coverage][Coverage 4.3 image]][CodeCov 4.3] | [![Code Coverage][Coverage 4.2 image]][CodeCov 4.2] | [![Code Coverage][Coverage 3.10 image]][CodeCov 3.10] | [![Code Coverage][Coverage 3.9 image]][CodeCov 3.9] |
8+
| N/A | N/A | [![Type Coverage][TypeCov image]][TypeCov] | N/A | N/A |
99

1010
Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features for database schema introspection and schema management.
1111

@@ -21,30 +21,38 @@ Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features f
2121
[AppVeyor 5.0]: https://ci.appveyor.com/project/doctrine/dbal/branch/5.0.x
2222
[AppVeyor 5.0 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/5.0.x?svg=true
2323
[GA 5.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A5.0.x
24-
[GA 5.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=5.0.x
24+
[GA 5.0 image]: https://github.com/doctrine/dbal/actions/workflows/continuous-integration.yml/badge.svg?branch=5.0.x
2525

2626
[Coverage 4.3 image]: https://codecov.io/gh/doctrine/dbal/branch/4.3.x/graph/badge.svg
2727
[4.3]: https://github.com/doctrine/dbal/tree/4.3.x
2828
[CodeCov 4.3]: https://codecov.io/gh/doctrine/dbal/branch/4.3.x
2929
[AppVeyor 4.3]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.3.x
3030
[AppVeyor 4.3 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.3.x?svg=true
3131
[GA 4.3]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.3.x
32-
[GA 4.3 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.3.x
32+
[GA 4.3 image]: https://github.com/doctrine/dbal/actions/workflows/continuous-integration.yml/badge.svg?branch=4.3.x
3333

3434
[Coverage 4.2 image]: https://codecov.io/gh/doctrine/dbal/branch/4.2.x/graph/badge.svg
3535
[4.2]: https://github.com/doctrine/dbal/tree/4.2.x
3636
[CodeCov 4.2]: https://codecov.io/gh/doctrine/dbal/branch/4.2.x
3737
[AppVeyor 4.2]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.2.x
3838
[AppVeyor 4.2 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.2.x?svg=true
3939
[GA 4.2]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.2.x
40-
[GA 4.2 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.2.x
40+
[GA 4.2 image]: https://github.com/doctrine/dbal/actions/workflows/continuous-integration.yml/badge.svg?branch=4.2.x
4141
[TypeCov]: https://shepherd.dev/github/doctrine/dbal
4242
[TypeCov image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg
4343

44+
[Coverage 3.10 image]: https://codecov.io/gh/doctrine/dbal/branch/3.10.x/graph/badge.svg
45+
[3.10]: https://github.com/doctrine/dbal/tree/3.10.x
46+
[CodeCov 3.10]: https://codecov.io/gh/doctrine/dbal/branch/3.10.x
47+
[AppVeyor 3.10]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.10.x
48+
[AppVeyor 3.10 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.10.x?svg=true
49+
[GA 3.10]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.10.x
50+
[GA 3.10 image]: https://github.com/doctrine/dbal/actions/workflows/continuous-integration.yml/badge.svg?branch=3.10.x
51+
4452
[Coverage 3.9 image]: https://codecov.io/gh/doctrine/dbal/branch/3.9.x/graph/badge.svg
4553
[3.9]: https://github.com/doctrine/dbal/tree/3.9.x
4654
[CodeCov 3.9]: https://codecov.io/gh/doctrine/dbal/branch/3.9.x
4755
[AppVeyor 3.9]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.9.x
4856
[AppVeyor 3.9 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.9.x?svg=true
4957
[GA 3.9]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.9.x
50-
[GA 3.9 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.9.x
58+
[GA 3.9 image]: https://github.com/doctrine/dbal/actions/workflows/continuous-integration.yml/badge.svg?branch=3.9.x

docs/en/reference/schema-representation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ and absolutely not portable.
9393
- **engine** (string): The DB engine used for the table. Currently only supported on MySQL.
9494

9595
- **unlogged** (boolean): Set a PostgreSQL table type as
96-
`unlogged <https://www.postgresql.org/docs/current/sql-createtable.htmll>`_
96+
`unlogged <https://www.postgresql.org/docs/current/sql-createtable.html>`_
9797

9898
Column
9999
~~~~~~

src/Connection.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ public function executeCacheQuery(string $sql, array $params, array $types, Quer
811811
}
812812

813813
if (isset($value[$realKey]) && $value[$realKey] instanceof ArrayResult) {
814-
return new Result($value[$realKey], $this);
814+
return new Result(clone $value[$realKey], $this);
815815
}
816816
} else {
817817
$value = [];
@@ -837,7 +837,7 @@ public function executeCacheQuery(string $sql, array $params, array $types, Quer
837837

838838
$resultCache->save($item);
839839

840-
return new Result($value[$realKey], $this);
840+
return new Result(clone $value[$realKey], $this);
841841
}
842842

843843
/**

tests/Connection/CachedQueryTest.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,60 @@
88
use Doctrine\DBAL\Cache\QueryCacheProfile;
99
use Doctrine\DBAL\Connection;
1010
use Doctrine\DBAL\Driver;
11+
use PHPUnit\Framework\Attributes\DataProvider;
1112
use PHPUnit\Framework\TestCase;
13+
use Psr\Cache\CacheItemPoolInterface;
1214
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1315

1416
class CachedQueryTest extends TestCase
1517
{
16-
public function testCachedQuery(): void
18+
#[DataProvider('providePsrCacheImplementations')]
19+
public function testCachedQuery(callable $psrCacheProvider): void
1720
{
18-
$cache = new ArrayAdapter();
21+
$cache = $psrCacheProvider();
1922

2023
$connection = $this->createConnection(1, ['foo'], [['bar']]);
2124
$qcp = new QueryCacheProfile(0, __FUNCTION__, $cache);
2225

23-
self::assertSame([['foo' => 'bar']], $connection->executeCacheQuery('SELECT 1', [], [], $qcp)
26+
$firstResult = $connection->executeCacheQuery('SELECT 1', [], [], $qcp);
27+
self::assertSame([['foo' => 'bar']], $firstResult
28+
->fetchAllAssociative());
29+
$firstResult->free();
30+
$secondResult = $connection->executeCacheQuery('SELECT 1', [], [], $qcp);
31+
self::assertSame([['foo' => 'bar']], $secondResult
2432
->fetchAllAssociative());
33+
$secondResult->free();
2534
self::assertSame([['foo' => 'bar']], $connection->executeCacheQuery('SELECT 1', [], [], $qcp)
2635
->fetchAllAssociative());
2736

2837
self::assertCount(1, $cache->getItem(__FUNCTION__)->get());
2938
}
3039

31-
public function testCachedQueryWithChangedImplementationIsExecutedTwice(): void
40+
#[DataProvider('providePsrCacheImplementations')]
41+
public function testCachedQueryWithChangedImplementationIsExecutedTwice(callable $psrCacheProvider): void
3242
{
3343
$connection = $this->createConnection(2, ['baz'], [['qux']]);
3444

3545
self::assertSame([['baz' => 'qux']], $connection->executeCacheQuery(
3646
'SELECT 1',
3747
[],
3848
[],
39-
new QueryCacheProfile(0, __FUNCTION__, new ArrayAdapter()),
49+
new QueryCacheProfile(0, __FUNCTION__, $psrCacheProvider()),
4050
)->fetchAllAssociative());
4151

4252
self::assertSame([['baz' => 'qux']], $connection->executeCacheQuery(
4353
'SELECT 1',
4454
[],
4555
[],
46-
new QueryCacheProfile(0, __FUNCTION__, new ArrayAdapter()),
56+
new QueryCacheProfile(0, __FUNCTION__, $psrCacheProvider()),
4757
)->fetchAllAssociative());
4858
}
4959

50-
public function testOldCacheFormat(): void
60+
#[DataProvider('providePsrCacheImplementations')]
61+
public function testOldCacheFormat(callable $psrCacheProvider): void
5162
{
5263
$connection = $this->createConnection(1, ['foo'], [['bar']]);
53-
$cache = new ArrayAdapter();
64+
$cache = $psrCacheProvider();
5465
$qcp = new QueryCacheProfile(0, __FUNCTION__, $cache);
5566

5667
[$cacheKey, $realKey] = $qcp->generateCacheKeys('SELECT 1', [], [], []);
@@ -83,4 +94,13 @@ private function createConnection(int $expectedQueryCount, array $columnNames, a
8394

8495
return new Connection([], $driver);
8596
}
97+
98+
/** @return array<non-empty-string, list<callable():CacheItemPoolInterface>> */
99+
public static function providePsrCacheImplementations(): array
100+
{
101+
return [
102+
'serialized' => [static fn () => new ArrayAdapter(0, true)],
103+
'by-reference' => [static fn () => new ArrayAdapter(0, false)],
104+
];
105+
}
86106
}

tests/ConnectionTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ protected function setUp(): void
4747

4848
private function getExecuteStatementMockConnection(): Connection&MockObject
4949
{
50-
$driverMock = $this->createMock(Driver::class);
50+
$driver = self::createStub(Driver::class);
5151

5252
return $this->getMockBuilder(Connection::class)
5353
->onlyMethods(['executeStatement'])
54-
->setConstructorArgs([[], $driverMock])
54+
->setConstructorArgs([[], $driver])
5555
->getMock();
5656
}
5757

@@ -146,9 +146,9 @@ public function testSetAutoCommit(): void
146146

147147
public function testConnectStartsTransactionInNoAutoCommitMode(): void
148148
{
149-
$driverMock = $this->createMock(Driver::class);
149+
$driver = self::createStub(Driver::class);
150150

151-
$conn = new Connection([], $driverMock);
151+
$conn = new Connection([], $driver);
152152

153153
$conn->setAutoCommit(false);
154154

@@ -161,9 +161,9 @@ public function testConnectStartsTransactionInNoAutoCommitMode(): void
161161

162162
public function testCommitStartsTransactionInNoAutoCommitMode(): void
163163
{
164-
$driverMock = $this->createMock(Driver::class);
164+
$driver = self::createStub(Driver::class);
165165

166-
$conn = new Connection([], $driverMock);
166+
$conn = new Connection([], $driver);
167167

168168
$conn->setAutoCommit(false);
169169
$conn->executeQuery('SELECT 1');
@@ -180,9 +180,9 @@ public static function resultProvider(): array
180180

181181
public function testRollBackStartsTransactionInNoAutoCommitMode(): void
182182
{
183-
$driverMock = $this->createMock(Driver::class);
183+
$driver = self::createStub(Driver::class);
184184

185-
$conn = new Connection([], $driverMock);
185+
$conn = new Connection([], $driver);
186186

187187
$conn->setAutoCommit(false);
188188
$conn->executeQuery('SELECT 1');
@@ -198,12 +198,12 @@ public function testSwitchingAutoCommitModeCommitsAllCurrentTransactions(): void
198198
->method('supportsSavepoints')
199199
->willReturn(true);
200200

201-
$driverMock = $this->createMock(Driver::class);
202-
$driverMock
201+
$driver = self::createStub(Driver::class);
202+
$driver
203203
->method('getDatabasePlatform')
204204
->willReturn($platform);
205205

206-
$conn = new Connection([], $driverMock);
206+
$conn = new Connection([], $driver);
207207

208208
$conn->beginTransaction();
209209
$conn->beginTransaction();

tests/Functional/TransactionTest.php

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
use Doctrine\DBAL\Connection;
88
use Doctrine\DBAL\Exception\ConnectionLost;
99
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
10+
use Doctrine\DBAL\Schema\Table;
1011
use Doctrine\DBAL\Tests\FunctionalTestCase;
12+
use Doctrine\DBAL\Types\Types;
1113

1214
use function func_get_args;
1315
use function restore_error_handler;
@@ -18,15 +20,6 @@
1820

1921
class TransactionTest extends FunctionalTestCase
2022
{
21-
protected function setUp(): void
22-
{
23-
if ($this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
24-
return;
25-
}
26-
27-
self::markTestSkipped('Restricted to MySQL.');
28-
}
29-
3023
public function testCommitFailure(): void
3124
{
3225
$this->expectConnectionLoss(static function (Connection $connection): void {
@@ -43,6 +36,10 @@ public function testRollbackFailure(): void
4336

4437
private function expectConnectionLoss(callable $scenario): void
4538
{
39+
if (! $this->connection->getDatabasePlatform() instanceof AbstractMySQLPlatform) {
40+
self::markTestSkipped('Restricted to MySQL.');
41+
}
42+
4643
$this->connection->executeStatement('SET SESSION wait_timeout=1');
4744
$this->connection->beginTransaction();
4845

@@ -67,4 +64,34 @@ private function expectConnectionLoss(callable $scenario): void
6764
restore_error_handler();
6865
}
6966
}
67+
68+
public function testNestedTransactionWalkthrough(): void
69+
{
70+
if (! $this->connection->getDatabasePlatform()->supportsSavepoints()) {
71+
self::markTestIncomplete('Broken when savepoints are not supported.');
72+
}
73+
74+
$table = new Table('storage');
75+
$table->addColumn('test_int', Types::INTEGER);
76+
$table->setPrimaryKey(['test_int']);
77+
78+
$this->dropAndCreateTable($table);
79+
80+
$query = 'SELECT count(test_int) FROM storage';
81+
82+
self::assertSame('0', (string) $this->connection->fetchOne($query));
83+
84+
$result = $this->connection->transactional(
85+
static fn (Connection $connection) => $connection->transactional(
86+
static function (Connection $connection) use ($query) {
87+
$connection->insert('storage', ['test_int' => 1]);
88+
89+
return $connection->fetchOne($query);
90+
},
91+
),
92+
);
93+
94+
self::assertSame('1', (string) $result);
95+
self::assertSame('1', (string) $this->connection->fetchOne($query));
96+
}
7097
}

tests/Query/Expression/ExpressionBuilderTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ class ExpressionBuilderTest extends TestCase
1616

1717
protected function setUp(): void
1818
{
19-
$conn = $this->createMock(Connection::class);
19+
$conn = self::createStub(Connection::class);
2020

2121
$this->expr = new ExpressionBuilder($conn);
2222

23-
$conn->expects(self::any())
23+
$conn
2424
->method('createExpressionBuilder')
2525
->willReturn($this->expr);
2626
}

tests/Types/DateImmutableTypeTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public function testConvertsDateStringToPHPValue(): void
9090

9191
public function testResetTimeFractionsWhenConvertingToPHPValue(): void
9292
{
93-
$this->platform->expects(self::any())
93+
$this->platform
9494
->method('getDateFormatString')
9595
->willReturn('Y-m-d');
9696

tests/Types/DateTimeImmutableTypeTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public function testConvertsDateTimeStringToPHPValue(): void
9090

9191
public function testConvertsDateTimeStringWithMicrosecondsToPHPValue(): void
9292
{
93-
$this->platform->expects(self::any())
93+
$this->platform
9494
->method('getDateTimeFormatString')
9595
->willReturn('Y-m-d H:i:s');
9696

tests/Types/TimeImmutableTypeTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public function testConvertsTimeStringToPHPValue(): void
9090

9191
public function testResetDateFractionsWhenConvertingToPHPValue(): void
9292
{
93-
$this->platform->expects(self::any())
93+
$this->platform
9494
->method('getTimeFormatString')
9595
->willReturn('H:i:s');
9696

0 commit comments

Comments
 (0)