-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle cached result column names and rows separately (#6504)
| Q | A |------------- | ----------- | Type | bug #### Summary Prior to #6428, there were two problems with caching results by design: 1. If the result contains no rows, its cached version won't return the number of columns. 2. If the result contains columns with the same name (e.g. `SELECT a.id, b.id FROM a, b`), they will clash even if fetched in the numeric mode. See #6428 (comment) for reference. The solution is: 1. Handle column names and rows in the cache result separately. This way, the column names are available even if there is no data. 2. Store rows as tuples (lists) instead of maps (associative arrays). This way, even if the result contains multiple columns with the same name, they can be correctly fetched with `fetchNumeric()`. **Additional notes**: 1. The changes in `TestUtil::generateResultSetQuery()` were necessary for the integration testing of the changes in `ArrayResult`. However, later, I realized that the modified code is database-agnostic, so running it in integration with each database platform would effectively test the same code path and would be redundant. I replaced the integration tests with the unit ones but left the change in `TestUtil` as it makes the calling code cleaner. 2. `ArrayStatementTest` was renamed to `ArrayResultTest`, which Git doesn't seem to recognize due to the amount of changes. **TODO**: - [x] Decide on the release version. This pull request changes the cache format, so the users will have to clear their cache during upgrade. I wouldn't consider it a BC break. We could also migrate the cache at runtime, but given that this cache can be cleared, I don't think it is worth the effort. - [x] Add upgrade documentation.
- Loading branch information
Showing
9 changed files
with
176 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\DBAL\Tests\Cache; | ||
|
||
use Doctrine\DBAL\Cache\ArrayResult; | ||
use Doctrine\DBAL\Exception\InvalidColumnIndex; | ||
use PHPUnit\Framework\Attributes\TestWith; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class ArrayResultTest extends TestCase | ||
{ | ||
private ArrayResult $result; | ||
|
||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->result = new ArrayResult(['username', 'active'], [ | ||
['jwage', true], | ||
['romanb', false], | ||
]); | ||
} | ||
|
||
public function testFree(): void | ||
{ | ||
self::assertSame(2, $this->result->rowCount()); | ||
|
||
$this->result->free(); | ||
|
||
self::assertSame(0, $this->result->rowCount()); | ||
} | ||
|
||
public function testColumnCount(): void | ||
{ | ||
self::assertSame(2, $this->result->columnCount()); | ||
} | ||
|
||
public function testColumnNames(): void | ||
{ | ||
self::assertSame('username', $this->result->getColumnName(0)); | ||
self::assertSame('active', $this->result->getColumnName(1)); | ||
} | ||
|
||
#[TestWith([2])] | ||
#[TestWith([-1])] | ||
public function testColumnNameWithInvalidIndex(int $index): void | ||
{ | ||
$this->expectException(InvalidColumnIndex::class); | ||
|
||
$this->result->getColumnName($index); | ||
} | ||
|
||
public function testRowCount(): void | ||
{ | ||
self::assertSame(2, $this->result->rowCount()); | ||
} | ||
|
||
public function testFetchAssociative(): void | ||
{ | ||
self::assertSame([ | ||
'username' => 'jwage', | ||
'active' => true, | ||
], $this->result->fetchAssociative()); | ||
} | ||
|
||
public function testFetchNumeric(): void | ||
{ | ||
self::assertSame(['jwage', true], $this->result->fetchNumeric()); | ||
} | ||
|
||
public function testFetchOne(): void | ||
{ | ||
self::assertSame('jwage', $this->result->fetchOne()); | ||
self::assertSame('romanb', $this->result->fetchOne()); | ||
} | ||
|
||
public function testFetchAllAssociative(): void | ||
{ | ||
self::assertSame([ | ||
[ | ||
'username' => 'jwage', | ||
'active' => true, | ||
], | ||
[ | ||
'username' => 'romanb', | ||
'active' => false, | ||
], | ||
], $this->result->fetchAllAssociative()); | ||
} | ||
|
||
public function testEmptyResult(): void | ||
{ | ||
$result = new ArrayResult(['a'], []); | ||
self::assertSame('a', $result->getColumnName(0)); | ||
} | ||
|
||
public function testSameColumnNames(): void | ||
{ | ||
$result = new ArrayResult(['a', 'a'], [[1, 2]]); | ||
|
||
self::assertSame('a', $result->getColumnName(0)); | ||
self::assertSame('a', $result->getColumnName(1)); | ||
|
||
self::assertEquals([1, 2], $result->fetchNumeric()); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.