Skip to content

Commit 6f98147

Browse files
Ignore subclasses without discriminatorValue when generating discriminator column condition SQL (#11200)
After commit 4e8e3ef when `\Doctrine\ORM\Query\SqlWalker` generates dicsriminator column condition SQL (method `\Doctrine\ORM\Query\SqlWalker::generateDiscriminatorColumnConditionSQL`) it adds an empty string to the list of possible values if the inheritance hierarchy contains a non-root abstract class. When the discriminator column is implemented with a custom type in PostgreSQL (equivalent of Enum) the query fails because the type cannot have a value of an empty string. It boils down to the fact that `\Doctrine\ORM\Mapping\ClassMetadataInfo::$subClasses` contains an abstract class and in its Metadata the value of `\Doctrine\ORM\Mapping\ClassMetadataInfo::$discriminatorValue` is `null`. #### Previous behavior In version 2.14.1 `\Doctrine\ORM\Mapping\ClassMetadataInfo::$subClasses` does not contain an abstract class. Fixes #11199, fixes #11177, fixes #10846. --------- Co-authored-by: Michael Skvortsov <michael.skvortsov@eleving.com> Co-authored-by: Matthias Pigulla <mp@webfactory.de>
1 parent cfadb54 commit 6f98147

File tree

2 files changed

+114
-6
lines changed

2 files changed

+114
-6
lines changed

src/Query/SqlWalker.php

+18-6
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str
471471
continue;
472472
}
473473

474+
$sqlTableAlias = $this->useSqlTableAliases
475+
? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
476+
: '';
477+
474478
$conn = $this->em->getConnection();
475479
$values = [];
476480

@@ -479,14 +483,22 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str
479483
}
480484

481485
foreach ($class->subClasses as $subclassName) {
482-
$values[] = $conn->quote($this->em->getClassMetadata($subclassName)->discriminatorValue);
483-
}
486+
$subclassMetadata = $this->em->getClassMetadata($subclassName);
484487

485-
$sqlTableAlias = $this->useSqlTableAliases
486-
? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.'
487-
: '';
488+
// Abstract entity classes show up in the list of subClasses, but may be omitted
489+
// from the discriminator map. In that case, they have a null discriminator value.
490+
if ($subclassMetadata->discriminatorValue === null) {
491+
continue;
492+
}
488493

489-
$sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')';
494+
$values[] = $conn->quote($subclassMetadata->discriminatorValue);
495+
}
496+
497+
if ($values !== []) {
498+
$sqlParts[] = $sqlTableAlias . $class->getDiscriminatorColumn()['name'] . ' IN (' . implode(', ', $values) . ')';
499+
} else {
500+
$sqlParts[] = '1=0'; // impossible condition
501+
}
490502
}
491503

492504
$sql = implode(' AND ', $sqlParts);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
use Doctrine\Tests\OrmFunctionalTestCase;
9+
use Generator;
10+
11+
class GH11199Test extends OrmFunctionalTestCase
12+
{
13+
protected function setUp(): void
14+
{
15+
parent::setUp();
16+
17+
$this->setUpEntitySchema([
18+
GH11199Root::class,
19+
GH11199Parent::class,
20+
GH11199Foo::class,
21+
GH11199Baz::class,
22+
GH11199AbstractLeaf::class,
23+
]);
24+
}
25+
26+
public function dqlStatements(): Generator
27+
{
28+
yield ['SELECT e FROM ' . GH11199Root::class . ' e', "/WHERE g0_.asset_type IN \('root', 'foo', 'baz'\)$/"];
29+
yield ['SELECT e FROM ' . GH11199Parent::class . ' e', "/WHERE g0_.asset_type IN \('foo'\)$/"];
30+
yield ['SELECT e FROM ' . GH11199Foo::class . ' e', "/WHERE g0_.asset_type IN \('foo'\)$/"];
31+
yield ['SELECT e FROM ' . GH11199Baz::class . ' e', "/WHERE g0_.asset_type IN \('baz'\)$/"];
32+
yield ['SELECT e FROM ' . GH11199AbstractLeaf::class . ' e', '/WHERE 1=0/'];
33+
}
34+
35+
/**
36+
* @dataProvider dqlStatements
37+
*/
38+
public function testGH11199(string $dql, string $expectedDiscriminatorValues): void
39+
{
40+
$query = $this->_em->createQuery($dql);
41+
$sql = $query->getSQL();
42+
43+
self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql);
44+
}
45+
}
46+
47+
/**
48+
* @ORM\Entity()
49+
* @ORM\Table(name="gh11199")
50+
* @ORM\InheritanceType("SINGLE_TABLE")
51+
* @ORM\DiscriminatorColumn(name="asset_type", type="string")
52+
* @ORM\DiscriminatorMap({
53+
* "root" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Root",
54+
* "foo" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Foo",
55+
* "baz" = "\Doctrine\Tests\ORM\Functional\Ticket\GH11199Baz",
56+
* })
57+
*/
58+
class GH11199Root
59+
{
60+
/**
61+
* @ORM\Id
62+
* @ORM\GeneratedValue(strategy="IDENTITY")
63+
* @ORM\Column(type="integer")
64+
*
65+
* @var int|null
66+
*/
67+
private $id = null;
68+
}
69+
70+
/**
71+
* @ORM\Entity()
72+
*/
73+
abstract class GH11199Parent extends GH11199Root
74+
{
75+
}
76+
77+
/**
78+
* @ORM\Entity()
79+
*/
80+
class GH11199Foo extends GH11199Parent
81+
{
82+
}
83+
84+
/**
85+
* @ORM\Entity()
86+
*/
87+
class GH11199Baz extends GH11199Root
88+
{
89+
}
90+
91+
/**
92+
* @ORM\Entity()
93+
*/
94+
abstract class GH11199AbstractLeaf extends GH11199Root
95+
{
96+
}

0 commit comments

Comments
 (0)