diff --git a/.phpstan-dba-mysqli.cache b/.phpstan-dba-mysqli.cache index 9c696337a..e36d9741e 100644 --- a/.phpstan-dba-mysqli.cache +++ b/.phpstan-dba-mysqli.cache @@ -471,6 +471,78 @@ )), ), ), + 'SELECT + CASE + WHEN freigabe1u1 > 50 THEN \'big-one\' + WHEN freigabe1u1 = 50 THEN \'normal\' + ELSE freigabe1u1 + END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT coalesce(COLUMN_NAME, "") as COLUMN_NAME, coalesce(EXTRA, "") as EXTRA, @@ -2092,6 +2164,86 @@ )), ), ), + 'SELECT CASE + WHEN freigabe1u1 > 50 THEN \'big-one\' + WHEN freigabe1u1 = 50 THEN \'normal + ELSE \'more\' END as val from ada' => + array ( + 'error' => + staabm\PHPStanDba\Error::__set_state(array( + 'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \'more\' END as val from ada LIMIT 0\' at line 4', + 'code' => 1064, + )), + ), + 'SELECT CASE + WHEN freigabe1u1 > 50 THEN \'big-one\' + WHEN freigabe1u1 = 50 THEN \'two\' ELSE \'more\' END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT CASE 1 WHEN 1 THEN \'one\' WHEN 2 THEN \'two\' ELSE \'more\' END as val from ada' => array ( 'result' => diff --git a/src/SqlAst/QueryScope.php b/src/SqlAst/QueryScope.php index 6c35b14aa..62de1c29a 100644 --- a/src/SqlAst/QueryScope.php +++ b/src/SqlAst/QueryScope.php @@ -12,7 +12,9 @@ use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use SqlFtw\Sql\Expression\BoolValue; +use SqlFtw\Sql\Expression\CaseExpression; use SqlFtw\Sql\Expression\ExpressionNode; use SqlFtw\Sql\Expression\FunctionCall; use SqlFtw\Sql\Expression\Identifier; @@ -112,6 +114,15 @@ public function getType($expression): Type throw new ShouldNotHappenException('Unable to resolve column '.$expression->getName()); } + if ($expression instanceof CaseExpression) { + $resultTypes = []; + foreach ($expression->getResults() as $result) { + $resultTypes[] = $this->getType($result); + } + + return TypeCombinator::union(...$resultTypes); + } + if ($expression instanceof FunctionCall) { foreach ($this->extensions as $extension) { if (!$extension->isFunctionSupported($expression)) { diff --git a/tests/default/config/.phpunit-phpstan-dba-mysqli.cache b/tests/default/config/.phpunit-phpstan-dba-mysqli.cache index 478c1f318..e90705429 100644 --- a/tests/default/config/.phpunit-phpstan-dba-mysqli.cache +++ b/tests/default/config/.phpunit-phpstan-dba-mysqli.cache @@ -91,6 +91,78 @@ )), ), ), + 'SELECT + CASE + WHEN freigabe1u1 > 50 THEN \'big-one\' + WHEN freigabe1u1 = 50 THEN \'normal\' + ELSE freigabe1u1 + END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT adaid FROM @@ -3526,6 +3598,73 @@ )), ), ), + 'SELECT CASE 1 WHEN 1 THEN \'one\' WHEN 2 THEN \'two\' ELSE \'more\' END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT COALESCE(freigabe1u1) as col from ada' => array ( 'result' => diff --git a/tests/default/config/.phpunit-phpstan-dba-pdo-mysql.cache b/tests/default/config/.phpunit-phpstan-dba-pdo-mysql.cache index f34d610f9..c4dd815dd 100644 --- a/tests/default/config/.phpunit-phpstan-dba-pdo-mysql.cache +++ b/tests/default/config/.phpunit-phpstan-dba-pdo-mysql.cache @@ -91,6 +91,78 @@ )), ), ), + 'SELECT + CASE + WHEN freigabe1u1 > 50 THEN \'big-one\' + WHEN freigabe1u1 = 50 THEN \'normal\' + ELSE freigabe1u1 + END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT adaid FROM @@ -3526,6 +3598,73 @@ )), ), ), + 'SELECT CASE 1 WHEN 1 THEN \'one\' WHEN 2 THEN \'two\' ELSE \'more\' END as val from ada' => + array ( + 'result' => + array ( + 5 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\UnionType::__set_state(array( + 'sortedTypes' => true, + 'cachedDescriptions' => + array ( + 2 => '0|\'val\'', + ), + 'types' => + array ( + 0 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + 1 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + ), + 'normalized' => false, + )), + 'itemType' => + PHPStan\Type\StringType::__set_state(array( + )), + 'allArrays' => NULL, + 'nextAutoIndexes' => + array ( + 0 => 1, + ), + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'objectType' => NULL, + 'arrayKeyType' => NULL, + 'value' => 'val', + 'isClassString' => false, + )), + 1 => + PHPStan\Type\Constant\ConstantIntegerType::__set_state(array( + 'value' => 0, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\StringType::__set_state(array( + )), + 1 => + PHPStan\Type\StringType::__set_state(array( + )), + ), + 'optionalKeys' => + array ( + ), + 'isList' => false, + )), + ), + ), 'SELECT COALESCE(freigabe1u1) as col from ada' => array ( 'result' => diff --git a/tests/default/data/sql-ast-narrowing.php b/tests/default/data/sql-ast-narrowing.php index 032997228..0f4ef4151 100644 --- a/tests/default/data/sql-ast-narrowing.php +++ b/tests/default/data/sql-ast-narrowing.php @@ -83,6 +83,20 @@ public function if(PDO $pdo): void assertType("PDOStatement", $stmt); } + public function caseWhen(PDO $pdo): void + { + $stmt = $pdo->query("SELECT CASE 1 WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'more' END as val from ada"); + assertType("PDOStatement", $stmt); + + $stmt = $pdo->query("SELECT + CASE + WHEN freigabe1u1 > 50 THEN 'big-one' + WHEN freigabe1u1 = 50 THEN 'normal' + ELSE freigabe1u1 + END as val from ada"); + assertType("PDOStatement, 0: 'big-one'|'normal'|int<-128, 127>}>", $stmt); // could be 'big-one'|'normal'|int<-128, 49> + } + public function concat(PDO $pdo): void { $stmt = $pdo->query('SELECT concat(akid, 5000) as col from ak');