Skip to content
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

Strange DBAL discrepancy between local env and CI #225

Closed
Seldaek opened this issue Feb 2, 2022 · 10 comments
Closed

Strange DBAL discrepancy between local env and CI #225

Seldaek opened this issue Feb 2, 2022 · 10 comments
Labels
bug Something isn't working

Comments

@Seldaek
Copy link
Contributor

Seldaek commented Feb 2, 2022

With this code here https://github.com/composer/packagist/blob/8a3d28e42f9790dea401e5f9afc7c2c9780b58d8/src/Entity/PackageRepository.php#L255-L270

Running locally I get:

Method App\Entity\PackageRepository::getStalePackagesForDumping() should return array<int, string> but returns array<int<0, max>, int<0, 4294967295>>.

Which makes sense given #222

But on CI e.g. https://github.com/composer/packagist/runs/5037284175?check_suite_focus=true I get this:

Method App\Entity\PackageRepository::getStalePackagesForDumping() should return array<int, string> but returns array<int, mixed>.

Any clue what could cause this? All dependency versions are the same as it installs from lock file.

@staabm
Copy link
Owner

staabm commented Feb 2, 2022

having a mixed only for a single column-type would mean you are running into this case:

$phpstanType = new MixedType();

are you sure you are using the same db schema locally and in CI?
maybe its a difference in mysql versions or maria db vs. mysql?

alternatively: maybe your schema is using a column type, which is not yet properly mapped to a phpstan type in

private function mapMysqlToPHPStanType(int $mysqlType, int $mysqlFlags, int $length): Type
{
$numeric = false;
$notNull = false;
$unsigned = false;
$autoIncrement = false;
foreach ($this->flags2txt($mysqlFlags) as $flag) {
switch ($flag) {
case 'NUM':
$numeric = true;
break;
case 'NOT_NULL':
$notNull = true;
break;
case 'AUTO_INCREMENT':
$autoIncrement = true;
break;
case 'UNSIGNED':
$unsigned = true;
break;
// ???
case 'PRI_KEY':
case 'PART_KEY':
case 'MULTIPLE_KEY':
case 'NO_DEFAULT_VALUE':
}
}
$phpstanType = null;
$mysqlIntegerRanges = new MysqlIntegerRanges();
if ($numeric) {
if ($unsigned) {
if (3 === $length) { // bool aka tinyint(1)
$phpstanType = $mysqlIntegerRanges->unsignedTinyInt();
}
if (4 === $length) {
$phpstanType = $mysqlIntegerRanges->unsignedTinyInt();
}
if (5 === $length) {
$phpstanType = $mysqlIntegerRanges->unsignedSmallInt();
}
if (8 === $length) {
$phpstanType = $mysqlIntegerRanges->unsignedMediumInt();
}
if (10 === $length) {
$phpstanType = $mysqlIntegerRanges->unsignedInt();
}
if (20 === $length) {
$phpstanType = $mysqlIntegerRanges->unsignedBigInt();
}
} else {
if (1 == $length) {
$phpstanType = $mysqlIntegerRanges->signedTinyInt();
}
if (4 === $length) {
$phpstanType = $mysqlIntegerRanges->signedTinyInt();
}
if (6 === $length) {
$phpstanType = $mysqlIntegerRanges->signedSmallInt();
}
if (9 === $length) {
$phpstanType = $mysqlIntegerRanges->signedMediumInt();
}
if (11 === $length) {
$phpstanType = $mysqlIntegerRanges->signedInt();
}
if (20 === $length) {
$phpstanType = $mysqlIntegerRanges->signedBigInt();
}
if (22 === $length) {
$phpstanType = $mysqlIntegerRanges->signedBigInt();
}
}
}
if ($autoIncrement) {
$phpstanType = $mysqlIntegerRanges->unsignedInt();
}
if (null === $phpstanType) {
switch ($this->type2txt($mysqlType)) {
case 'DOUBLE':
case 'NEWDECIMAL':
$phpstanType = new FloatType();
break;
case 'LONGLONG':
case 'LONG':
case 'SHORT':
case 'YEAR':
case 'BIT':
case 'INT24':
$phpstanType = new IntegerType();
break;
case 'BLOB':
case 'CHAR':
case 'STRING':
case 'VAR_STRING':
case 'JSON':
case 'DATE':
case 'TIME':
case 'DATETIME':
case 'TIMESTAMP':
$phpstanType = new StringType();
break;
default:
$phpstanType = new MixedType();
}
}
if (false === $notNull) {
$phpstanType = TypeCombinator::addNull($phpstanType);
}
return $phpstanType;

@staabm staabm added the bug Something isn't working label Feb 2, 2022
@Seldaek
Copy link
Contributor Author

Seldaek commented Feb 2, 2022

The package table has a simple int(11) primary id, nothing weird, and it definitely hasn't changed in 10 years so I believe my local schema matches CI :)

https://github.com/composer/packagist/blob/8a3d28e42f9790dea401e5f9afc7c2c9780b58d8/src/Entity/Package.php#L62-L67

I'm on mysql8.0.13 and GH is on 8.0.26 but I doubt that'd make a difference here.. I can try to upgrade to be sure, wouldn't hurt anyway ;)

@staabm
Copy link
Owner

staabm commented Feb 2, 2022

If time allows I can debug the CI job and the used phpstan-dba version within the CI pipeline.

@Seldaek
Copy link
Contributor Author

Seldaek commented Feb 2, 2022

On 8.0.28 locally now, still can't repro :/

@Seldaek
Copy link
Contributor Author

Seldaek commented Feb 2, 2022

Very confused, just tried again to run phpstan and suddenly it changed to the same output as CI?!

I even tried clearing PHPStan & phpstan-dba caches earlier, it didn't help.. proper WTF here.. but at least it works now so I guess closing 🤷‍♂️

@Seldaek Seldaek closed this as completed Feb 2, 2022
@staabm
Copy link
Owner

staabm commented Feb 2, 2022

hm ok, so you are now able to debug locally why its returning mixed?

running phpstan with --debug disables all caches.. thats the first thing I try when caching might be a problem.

@staabm
Copy link
Owner

staabm commented Feb 3, 2022

I have a new theory, why you get a array<int, mixed>,..

doctrine defines the method fetchFirstColumn on Connection to return list<mixed>:
https://github.com/doctrine/dbal/blob/0dc504e80b400870d4d1d0392394164b4dd39019/src/Connection.php#L888-L902

in your example phpstan-dba is taking one of the return $defaultReturn branches and therefore defines the return-type to whatever doctrine-dbal itself defines at the phpdoc level

public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
$args = $methodCall->getArgs();
$defaultReturn = ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->getArgs(),
$methodReflection->getVariants(),
)->getReturnType();
if (\count($args) < 2) {
return $defaultReturn;
}
if ($scope->getType($args[0]->value) instanceof MixedType) {
return $defaultReturn;
}
// make sure we don't report wrong types in doctrine 2.x
if (!InstalledVersions::satisfies(new VersionParser(), 'doctrine/dbal', '3.*')) {
return $defaultReturn;
}
$resultType = $this->inferType($methodReflection, $args[0]->value, $args[1]->value, $scope);
if (null !== $resultType) {
return $resultType;
}
return $defaultReturn;
}

I am not yet sure for the exact reason.

@staabm
Copy link
Owner

staabm commented Feb 4, 2022

just installed packagist locally and did some debugging.

the reason for this query not beeing analyzable is the following mysql error:

grafik

phpstan-dba simulates a value which does not fit to the mysql native type and therefore we produce a sql error

@staabm
Copy link
Owner

staabm commented Feb 4, 2022

I think we can consider this issue fixed.

the acual problem was, that we skipped everything which had no parameters.

@Seldaek
Copy link
Contributor Author

Seldaek commented Feb 4, 2022

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants