Skip to content

Commit

Permalink
QueryResultTypeWalker: fix nullability checks over unknown type
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored and ondrejmirtes committed Jun 25, 2024
1 parent 3d10eab commit ad91388
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 12 deletions.
24 changes: 12 additions & 12 deletions src/Type/Doctrine/Query/QueryResultTypeWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ public function walkFunction($function): string
new FloatType()
);

if (TypeCombinator::containsNull($exprType)) {
if ($this->canBeNull($exprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -388,7 +388,7 @@ public function walkFunction($function): string
$secondExprType = $this->unmarshalType($function->secondArithmetic->dispatch($this));

$type = IntegerRangeType::fromInterval(0, null);
if (TypeCombinator::containsNull($firstExprType) || TypeCombinator::containsNull($secondExprType)) {
if ($this->canBeNull($firstExprType) || $this->canBeNull($secondExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -399,7 +399,7 @@ public function walkFunction($function): string

foreach ($function->concatExpressions as $expr) {
$type = $this->unmarshalType($expr->dispatch($this));
$hasNull = $hasNull || TypeCombinator::containsNull($type);
$hasNull = $hasNull || $this->canBeNull($type);
}

$type = new StringType();
Expand All @@ -420,7 +420,7 @@ public function walkFunction($function): string
$intervalExprType = $this->unmarshalType($function->intervalExpression->dispatch($this));

$type = new StringType();
if (TypeCombinator::containsNull($dateExprType) || TypeCombinator::containsNull($intervalExprType)) {
if ($this->canBeNull($dateExprType) || $this->canBeNull($intervalExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -434,7 +434,7 @@ public function walkFunction($function): string
new IntegerType(),
new FloatType()
);
if (TypeCombinator::containsNull($date1ExprType) || TypeCombinator::containsNull($date2ExprType)) {
if ($this->canBeNull($date1ExprType) || $this->canBeNull($date2ExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -444,7 +444,7 @@ public function walkFunction($function): string
$stringPrimaryType = $this->unmarshalType($function->stringPrimary->dispatch($this));

$type = IntegerRangeType::fromInterval(0, null);
if (TypeCombinator::containsNull($stringPrimaryType)) {
if ($this->canBeNull($stringPrimaryType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -455,7 +455,7 @@ public function walkFunction($function): string
$secondExprType = $this->unmarshalType($this->walkStringPrimary($function->secondStringPrimary));

$type = IntegerRangeType::fromInterval(0, null);
if (TypeCombinator::containsNull($firstExprType) || TypeCombinator::containsNull($secondExprType)) {
if ($this->canBeNull($firstExprType) || $this->canBeNull($secondExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -467,7 +467,7 @@ public function walkFunction($function): string
$stringPrimaryType = $this->unmarshalType($function->stringPrimary->dispatch($this));

$type = new StringType();
if (TypeCombinator::containsNull($stringPrimaryType)) {
if ($this->canBeNull($stringPrimaryType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -478,7 +478,7 @@ public function walkFunction($function): string
$secondExprType = $this->unmarshalType($this->walkSimpleArithmeticExpression($function->secondSimpleArithmeticExpression));

$type = IntegerRangeType::fromInterval(0, null);
if (TypeCombinator::containsNull($firstExprType) || TypeCombinator::containsNull($secondExprType)) {
if ($this->canBeNull($firstExprType) || $this->canBeNull($secondExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -493,7 +493,7 @@ public function walkFunction($function): string
$exprType = $this->unmarshalType($this->walkSimpleArithmeticExpression($function->simpleArithmeticExpression));

$type = new FloatType();
if (TypeCombinator::containsNull($exprType)) {
if ($this->canBeNull($exprType)) {
$type = TypeCombinator::addNull($type);
}

Expand All @@ -510,7 +510,7 @@ public function walkFunction($function): string
}

$type = new StringType();
if (TypeCombinator::containsNull($stringType) || TypeCombinator::containsNull($firstExprType) || TypeCombinator::containsNull($secondExprType)) {
if ($this->canBeNull($stringType) || $this->canBeNull($firstExprType) || $this->canBeNull($secondExprType)) {
$type = TypeCombinator::addNull($type);
}

Expand Down Expand Up @@ -714,7 +714,7 @@ public function walkCoalesceExpression($coalesceExpression): string
}

$type = $this->unmarshalType($expression->dispatch($this));
$allTypesContainNull = $allTypesContainNull && TypeCombinator::containsNull($type);
$allTypesContainNull = $allTypesContainNull && $this->canBeNull($type);

// Some drivers manipulate the types, lets avoid false positives by generalizing constant types
// e.g. sqlsrv: "COALESCE returns the data type of value with the highest precedence"
Expand Down
16 changes: 16 additions & 0 deletions tests/Type/Doctrine/Query/QueryResultTypeWalkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use DateTime;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Types\Type as DbalType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Tools\SchemaTool;
Expand Down Expand Up @@ -44,6 +45,7 @@
use QueryResult\EntitiesEnum\IntEnum;
use QueryResult\EntitiesEnum\StringEnum;
use Throwable;
use Type\Doctrine\data\QueryResult\CustomIntType;
use function array_merge;
use function array_shift;
use function assert;
Expand Down Expand Up @@ -76,6 +78,10 @@ public static function setUpBeforeClass(): void
$em = require __DIR__ . '/../data/QueryResult/entity-manager.php';
self::$em = $em;

if (!DbalType::hasType(CustomIntType::NAME)) {
DbalType::addType(CustomIntType::NAME, CustomIntType::class);
}

$schemaTool = new SchemaTool($em);
$classes = $em->getMetadataFactory()->getAllMetadata();
$schemaTool->createSchema($classes);
Expand Down Expand Up @@ -1241,6 +1247,16 @@ public function getTestData(): iterable
',
];

yield 'abs function with mixed' => [
$this->constantArray([
[new ConstantIntegerType(1), TypeCombinator::addNull($this->unumericStringified())],
]),
'
SELECT ABS(o.mixedColumn)
FROM QueryResult\Entities\One o
',
];

yield 'bit_and function' => [
$this->constantArray([
[new ConstantIntegerType(1), $this->uintStringified()],
Expand Down
17 changes: 17 additions & 0 deletions tests/Type/Doctrine/data/QueryResult/CustomIntType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);

namespace Type\Doctrine\data\QueryResult;

use Doctrine\DBAL\Types\IntegerType;

class CustomIntType extends IntegerType
{

public const NAME = 'custom_int';

public function getName(): string
{
return self::NAME;
}

}
7 changes: 7 additions & 0 deletions tests/Type/Doctrine/data/QueryResult/Entities/One.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ class One
*/
public $embedded;

/**
* @Column(type="custom_int", nullable=true)
*
* @var mixed
*/
public $mixedColumn;

public function __construct()
{
$this->subOne = new SubOne();
Expand Down

0 comments on commit ad91388

Please sign in to comment.