Skip to content

QueryResultTypeWalker: fix nullability checks over unknown type #584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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