Skip to content

Commit

Permalink
Merge pull request #200 from thecodingmachine/fix/findFromRaxSql-count
Browse files Browse the repository at this point in the history
findFromRaw sql count
  • Loading branch information
moufmouf committed Apr 27, 2020
2 parents fd8845b + 9e04377 commit 2d4e6f6
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -13,4 +13,5 @@ vendor/*
/.travis/
/vendor-bin/require-checker/vendor/
/vendor-bin/couscous/vendor/
.phpunit.result.cache
.phpunit.result.cache
tdbm.lock.yml
29 changes: 23 additions & 6 deletions src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php
Expand Up @@ -144,14 +144,16 @@ private function processParsedUnionQuery(array $parsedSql, ?string $sqlCount): a
private function processParsedSelectQuery(array $parsedSql, ?string $sqlCount): array
{
// 1: let's reformat the SELECT and construct our columns
list($select, $columnDescriptors) = $this->formatSelect($parsedSql['SELECT']);
list($select, $countSelect, $columnDescriptors) = $this->formatSelect($parsedSql['SELECT']);
$generator = new PHPSQLCreator();
$parsedSql['SELECT'] = $select;
$processedSql = $generator->create($parsedSql);

// 2: let's compute the count query if needed
if ($sqlCount === null) {
$parsedSqlCount = $this->generateParsedSqlCount($parsedSql);
$parsedCountSql = $parsedSql;
$parsedCountSql['SELECT'] = $countSelect;
$parsedSqlCount = $this->generateParsedSqlCount($parsedCountSql);
$processedSqlCount = $generator->create($parsedSqlCount);
} else {
$processedSqlCount = $sqlCount;
Expand All @@ -173,40 +175,46 @@ private function formatSelect(array $baseSelect): array

$connection = $this->tdbmService->getConnection();
$formattedSelect = [];
$formattedCountSelect = [];
$columnDescriptors = [];
$fetchedTables = [];

foreach ($baseSelect as $entry) {
if ($entry['expr_type'] !== 'colref') {
$formattedSelect[] = $entry;
$formattedCountSelect[] = $entry;
continue;
}

$noQuotes = $entry['no_quotes'];
if ($noQuotes['delim'] !== '.' || count($noQuotes['parts']) !== 2) {
$formattedSelect[] = $entry;
$formattedCountSelect[] = $entry;
continue;
}

$tableName = $noQuotes['parts'][0];
if (!in_array($tableName, $relatedTables)) {
$formattedSelect[] = $entry;
$formattedCountSelect[] = $entry;
continue;
}

$columnName = $noQuotes['parts'][1];
if ($columnName !== '*') {
$formattedSelect[] = $entry;
$formattedCountSelect[] = $entry;
continue;
}

$table = $this->schema->getTable($tableName);
$pkColumns = $table->getPrimaryKeyColumns();
foreach ($table->getColumns() as $column) {
$columnName = $column->getName();
$alias = "{$tableName}____{$columnName}";
$formattedSelect[] = [
$astColumn = [
'expr_type' => 'colref',
'base_expr' => $connection->quoteIdentifier($tableName).'.'.$connection->quoteIdentifier($columnName),
'base_expr' => $connection->quoteIdentifier($tableName) . '.' . $connection->quoteIdentifier($columnName),
'no_quotes' => [
'delim' => '.',
'parts' => [
Expand All @@ -219,7 +227,10 @@ private function formatSelect(array $baseSelect): array
'name' => $alias,
]
];

$formattedSelect[] = $astColumn;
if (in_array($columnName, $pkColumns)) {
$formattedCountSelect[] = $astColumn;
}
$columnDescriptors[$alias] = [
'as' => $alias,
'table' => $tableName,
Expand All @@ -241,7 +252,13 @@ private function formatSelect(array $baseSelect): array
$formattedSelect[$i]['delim'] = ',';
}
}
return [$formattedSelect, $columnDescriptors];

for ($i = 0; $i < count($formattedCountSelect) - 1; $i++) {
if (!isset($formattedCountSelect[$i]['delim'])) {
$formattedCountSelect[$i]['delim'] = ',';
}
}
return [$formattedSelect, $formattedCountSelect, $columnDescriptors];
}

/**
Expand Down
33 changes: 33 additions & 0 deletions tests/Dao/TestAlbumDao.php
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);

namespace TheCodingMachine\TDBM\Dao;

use TheCodingMachine\TDBM\Test\Dao\Bean\AlbumBean;
use TheCodingMachine\TDBM\Test\Dao\Generated\AlbumBaseDao;

/**
* The AlbumDao class will maintain the persistence of UserBean class into the users table.
*/
class TestAlbumDao extends AlbumBaseDao
{
/**
* @return \TheCodingMachine\TDBM\ResultIterator|AlbumBean[]
*/
public function findAllFromRawSql()
{
return $this->findFromRawSql('SELECT DISTINCT albums.* FROM albums');
}

/**
* @return \TheCodingMachine\TDBM\ResultIterator|AlbumBean[]
*/
public function findAllFromRawSqlWithCount()
{
return $this->findFromRawSql(
'SELECT DISTINCT albums.* FROM albums',
[],
'SELECT COUNT(DISTINCT albums.id) FROM albums'
);
}
}
29 changes: 29 additions & 0 deletions tests/Dao/TestPersonDao.php
@@ -0,0 +1,29 @@
<?php
/*
* This file has been automatically generated by TDBM.
* You can edit this file as it will not be overwritten.
*/

declare(strict_types=1);

namespace TheCodingMachine\TDBM\Dao;

use TheCodingMachine\TDBM\ResultIterator;
use TheCodingMachine\TDBM\Test\Dao\Generated\PersonBaseDao;

/**
* The ContactDao class will maintain the persistence of ContactBean class into the contact table.
*/
class TestPersonDao extends PersonBaseDao
{
public function testFindFromRawSQLOnInherited(): ResultIterator
{
$sql = '
SELECT DISTINCT person.*, contact.*, users.*
FROM person JOIN contact ON person.id = contact.id
JOIN users ON contact.id = users.id
';

return $this->findFromRawSql($sql, []);
}
}
27 changes: 27 additions & 0 deletions tests/TDBMDaoGeneratorTest.php
Expand Up @@ -30,8 +30,10 @@
use Ramsey\Uuid\Uuid;
use ReflectionClass;
use ReflectionMethod;
use TheCodingMachine\TDBM\Dao\TestAlbumDao;
use TheCodingMachine\TDBM\Dao\TestArticleDao;
use TheCodingMachine\TDBM\Dao\TestCountryDao;
use TheCodingMachine\TDBM\Dao\TestPersonDao;
use TheCodingMachine\TDBM\Dao\TestRoleDao;
use TheCodingMachine\TDBM\Dao\TestUserDao;
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserDaoInterface;
Expand All @@ -42,6 +44,7 @@
use TheCodingMachine\TDBM\Test\Dao\ArtistDao;
use TheCodingMachine\TDBM\Test\Dao\BaseObjectDao;
use TheCodingMachine\TDBM\Test\Dao\Bean\AccountBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\AlbumBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\AllNullableBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\AnimalBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\Article2Bean;
Expand Down Expand Up @@ -2178,4 +2181,28 @@ public function testFindByDateTime(): void
$personDao->findByModifiedAt(new \DateTimeImmutable())->count();
$this->assertTrue(true);
}

/**
* Bug: find from sql use a `COUNT(DISTINCT *)` which fails because of null values.
*/
public function testFindFromRawSqlCount(): void
{
$dao = new TestAlbumDao($this->tdbmService);
$albums = $dao->findAllFromRawSql();

$firstAlbum = $albums->first();
assert($firstAlbum instanceof AlbumBean);
$this->assertNull($firstAlbum->getNode()); // This null ensure reproducibility of the bug
$expectedCount = $dao->findAllFromRawSqlWithCount()->count();
$this->assertEquals($expectedCount, $albums->count());
}

public function testFindFromRawSQLOnInheritance(): void
{
$dao = new TestPersonDao($this->tdbmService);
$objects = $dao->testFindFromRawSQLOnInherited();

$this->assertNotNull($objects->first());
$this->assertEquals(6, $objects->count());
}
}

0 comments on commit 2d4e6f6

Please sign in to comment.