From a2e789ff09fe3bfc34e10db9dc33cbb3d180badd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 11:17:51 +0100 Subject: [PATCH 01/11] Report invalid query after simulation as unresolvable --- src/QueryReflection/QueryReflection.php | 5 +++++ ...vableQueryInvalidAfterSimulationException.php | 16 ++++++++++++++++ tests/rules/UnresolvableQueryMethodRuleTest.php | 9 ++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/UnresolvableQueryInvalidAfterSimulationException.php diff --git a/src/QueryReflection/QueryReflection.php b/src/QueryReflection/QueryReflection.php index 1f3f06e96..1245b5d88 100644 --- a/src/QueryReflection/QueryReflection.php +++ b/src/QueryReflection/QueryReflection.php @@ -32,6 +32,7 @@ use staabm\PHPStanDba\SchemaReflection\SchemaReflection; use staabm\PHPStanDba\SqlAst\ParserInference; use staabm\PHPStanDba\UnresolvableQueryException; +use staabm\PHPStanDba\UnresolvableQueryInvalidAfterSimulationException; final class QueryReflection { @@ -297,6 +298,10 @@ public function resolveQueryStrings(Expr $queryExpr, Scope $scope): iterable $error = $this->validateQueryString($normalizedQuery); if ($error === null) { yield $normalizedQuery; + } else { + if (QueryReflection::getRuntimeConfiguration()->isDebugEnabled()) { + throw new UnresolvableQueryInvalidAfterSimulationException('Seems the query is too dynamic to be resolved by query simulation'); + } } } } diff --git a/src/UnresolvableQueryInvalidAfterSimulationException.php b/src/UnresolvableQueryInvalidAfterSimulationException.php new file mode 100644 index 000000000..0a2125bd2 --- /dev/null +++ b/src/UnresolvableQueryInvalidAfterSimulationException.php @@ -0,0 +1,16 @@ +analyse([__DIR__ . '/data/bug-547.php'], []); + $this->analyse([__DIR__ . '/data/bug-547.php'], [ + [ + 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', + 10, + UnresolvableQueryInvalidAfterSimulationException::getTip(), + ], + ]); } public function testBug676(): void From 42aa482560cd1ada145f635ef8e1243ac3f52871 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 11:20:46 +0100 Subject: [PATCH 02/11] fix --- tests/rules/UnresolvableQueryMethodRuleTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/rules/UnresolvableQueryMethodRuleTest.php b/tests/rules/UnresolvableQueryMethodRuleTest.php index 995216a08..02b379522 100644 --- a/tests/rules/UnresolvableQueryMethodRuleTest.php +++ b/tests/rules/UnresolvableQueryMethodRuleTest.php @@ -72,7 +72,13 @@ public function testBug536(): void public function testBug548(): void { - $this->analyse([__DIR__ . '/data/bug-548.php'], []); + $this->analyse([__DIR__ . '/data/bug-548.php'], [ + [ + 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', + 10, + UnresolvableQueryInvalidAfterSimulationException::getTip(), + ], + ]); } public function testBug547(): void From 131eb6a3a9a077828a103d131721cfffa8dabbd5 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 11:30:22 +0100 Subject: [PATCH 03/11] added more tests --- tests/rules/data/syntax-error-in-query-method.php | 8 ++++++++ tests/rules/data/unresolvable-query-in-method.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/rules/data/syntax-error-in-query-method.php b/tests/rules/data/syntax-error-in-query-method.php index 89a5a27f5..6f29ab1ab 100644 --- a/tests/rules/data/syntax-error-in-query-method.php +++ b/tests/rules/data/syntax-error-in-query-method.php @@ -149,4 +149,12 @@ public function bug458(PDO $pdo) $pdo->query('SELECT * FROM ' . $table . ' LIMIT 1'); } + public function writes(PDO $pdo, int $adaid): void + { + $pdo->query('UPDATE `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('INSERT INTO `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('REPLACE INTO `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('DELETE FROM `ada` WHERE adaid = '.$adaid); + } + } diff --git a/tests/rules/data/unresolvable-query-in-method.php b/tests/rules/data/unresolvable-query-in-method.php index e596a82ab..6c1f8e180 100644 --- a/tests/rules/data/unresolvable-query-in-method.php +++ b/tests/rules/data/unresolvable-query-in-method.php @@ -38,4 +38,12 @@ public function stringQueryFragment(PDO $pdo, string $string) { $pdo->query('SELECT email FROM ada WHERE '.$string); } + + public function writes(PDO $pdo, int $adaid): void + { + $pdo->query('UPDATE `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('INSERT INTO `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('REPLACE INTO `ada` SET email="test" WHERE adaid = '.$adaid); + $pdo->query('DELETE FROM `ada` WHERE adaid = '.$adaid); + } } From 680d928be68a9eddbce3687ceac9952c711d1f57 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 15:47:28 +0100 Subject: [PATCH 04/11] Restore src/QueryReflection/QueryReflection.php --- src/QueryReflection/QueryReflection.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/QueryReflection/QueryReflection.php b/src/QueryReflection/QueryReflection.php index 1245b5d88..1f3f06e96 100644 --- a/src/QueryReflection/QueryReflection.php +++ b/src/QueryReflection/QueryReflection.php @@ -32,7 +32,6 @@ use staabm\PHPStanDba\SchemaReflection\SchemaReflection; use staabm\PHPStanDba\SqlAst\ParserInference; use staabm\PHPStanDba\UnresolvableQueryException; -use staabm\PHPStanDba\UnresolvableQueryInvalidAfterSimulationException; final class QueryReflection { @@ -298,10 +297,6 @@ public function resolveQueryStrings(Expr $queryExpr, Scope $scope): iterable $error = $this->validateQueryString($normalizedQuery); if ($error === null) { yield $normalizedQuery; - } else { - if (QueryReflection::getRuntimeConfiguration()->isDebugEnabled()) { - throw new UnresolvableQueryInvalidAfterSimulationException('Seems the query is too dynamic to be resolved by query simulation'); - } } } } From 3dae45d007eda42ff223e930712ad64b254ab47b Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 15:47:36 +0100 Subject: [PATCH 05/11] Delete UnresolvableQueryInvalidAfterSimulationException.php --- ...vableQueryInvalidAfterSimulationException.php | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/UnresolvableQueryInvalidAfterSimulationException.php diff --git a/src/UnresolvableQueryInvalidAfterSimulationException.php b/src/UnresolvableQueryInvalidAfterSimulationException.php deleted file mode 100644 index 0a2125bd2..000000000 --- a/src/UnresolvableQueryInvalidAfterSimulationException.php +++ /dev/null @@ -1,16 +0,0 @@ - Date: Sun, 26 Feb 2023 15:48:21 +0100 Subject: [PATCH 06/11] Restore tests/rules/UnresolvableQueryMethodRuleTest.php --- tests/rules/UnresolvableQueryMethodRuleTest.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/tests/rules/UnresolvableQueryMethodRuleTest.php b/tests/rules/UnresolvableQueryMethodRuleTest.php index 02b379522..ed24966ea 100644 --- a/tests/rules/UnresolvableQueryMethodRuleTest.php +++ b/tests/rules/UnresolvableQueryMethodRuleTest.php @@ -8,7 +8,6 @@ use PHPStan\Testing\RuleTestCase; use staabm\PHPStanDba\QueryReflection\QueryReflection; use staabm\PHPStanDba\Rules\SyntaxErrorInQueryMethodRule; -use staabm\PHPStanDba\UnresolvableQueryInvalidAfterSimulationException; use staabm\PHPStanDba\UnresolvableQueryMixedTypeException; use staabm\PHPStanDba\UnresolvableQueryStringTypeException; @@ -72,24 +71,12 @@ public function testBug536(): void public function testBug548(): void { - $this->analyse([__DIR__ . '/data/bug-548.php'], [ - [ - 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', - 10, - UnresolvableQueryInvalidAfterSimulationException::getTip(), - ], - ]); + $this->analyse([__DIR__ . '/data/bug-548.php'], []); } public function testBug547(): void { - $this->analyse([__DIR__ . '/data/bug-547.php'], [ - [ - 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', - 10, - UnresolvableQueryInvalidAfterSimulationException::getTip(), - ], - ]); + $this->analyse([__DIR__ . '/data/bug-547.php'], []); } public function testBug676(): void From e48de8b08759059db1084ab9d062a6b0208e3aa3 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 16:03:59 +0100 Subject: [PATCH 07/11] Revert "Delete UnresolvableQueryInvalidAfterSimulationException.php" This reverts commit 1748524e2323337921377eaa81070373394a8cff. --- ...vableQueryInvalidAfterSimulationException.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/UnresolvableQueryInvalidAfterSimulationException.php diff --git a/src/UnresolvableQueryInvalidAfterSimulationException.php b/src/UnresolvableQueryInvalidAfterSimulationException.php new file mode 100644 index 000000000..0a2125bd2 --- /dev/null +++ b/src/UnresolvableQueryInvalidAfterSimulationException.php @@ -0,0 +1,16 @@ + Date: Sun, 26 Feb 2023 16:04:19 +0100 Subject: [PATCH 08/11] Revert "Restore tests/rules/UnresolvableQueryMethodRuleTest.php" This reverts commit 3f62618acc9901d58e96180b6561d00823b181d3. --- tests/rules/UnresolvableQueryMethodRuleTest.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/rules/UnresolvableQueryMethodRuleTest.php b/tests/rules/UnresolvableQueryMethodRuleTest.php index ed24966ea..02b379522 100644 --- a/tests/rules/UnresolvableQueryMethodRuleTest.php +++ b/tests/rules/UnresolvableQueryMethodRuleTest.php @@ -8,6 +8,7 @@ use PHPStan\Testing\RuleTestCase; use staabm\PHPStanDba\QueryReflection\QueryReflection; use staabm\PHPStanDba\Rules\SyntaxErrorInQueryMethodRule; +use staabm\PHPStanDba\UnresolvableQueryInvalidAfterSimulationException; use staabm\PHPStanDba\UnresolvableQueryMixedTypeException; use staabm\PHPStanDba\UnresolvableQueryStringTypeException; @@ -71,12 +72,24 @@ public function testBug536(): void public function testBug548(): void { - $this->analyse([__DIR__ . '/data/bug-548.php'], []); + $this->analyse([__DIR__ . '/data/bug-548.php'], [ + [ + 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', + 10, + UnresolvableQueryInvalidAfterSimulationException::getTip(), + ], + ]); } public function testBug547(): void { - $this->analyse([__DIR__ . '/data/bug-547.php'], []); + $this->analyse([__DIR__ . '/data/bug-547.php'], [ + [ + 'Unresolvable Query: Seems the query is too dynamic to be resolved by query simulation.', + 10, + UnresolvableQueryInvalidAfterSimulationException::getTip(), + ], + ]); } public function testBug676(): void From 87340529790e19c1d5e6fb85d2e838d8be095b64 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 16:16:14 +0100 Subject: [PATCH 09/11] expect errors --- tests/rules/SyntaxErrorInQueryMethodRuleTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/rules/SyntaxErrorInQueryMethodRuleTest.php b/tests/rules/SyntaxErrorInQueryMethodRuleTest.php index 47c8386fa..a239ffef3 100644 --- a/tests/rules/SyntaxErrorInQueryMethodRuleTest.php +++ b/tests/rules/SyntaxErrorInQueryMethodRuleTest.php @@ -100,6 +100,14 @@ public function testSyntaxErrorInQueryRule(): void "Query error: Table 'phpstan_dba.adasfd' doesn't exist (1146).", 138, ], + [ + "Query error: 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 'WHERE adaid = 1' at line 1 (1064).", + 155, + ], + [ + "Query error: 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 'WHERE adaid = 1' at line 1 (1064).", + 156, + ], ]; } elseif (PdoMysqlQueryReflector::NAME === getenv('DBA_REFLECTOR')) { $expected = [ From 782f7d69c6f2d81df6e13cb775d846cfdf3b5b10 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 26 Feb 2023 16:32:45 +0100 Subject: [PATCH 10/11] throw unresolvable query while query resolving --- src/QueryReflection/QueryReflection.php | 16 +++++++++------- src/UnresolvableQueryDynamicFromException.php | 16 ++++++++++++++++ ...vableQueryInvalidAfterSimulationException.php | 16 ---------------- tests/rules/UnresolvableQueryMethodRuleTest.php | 6 +++--- 4 files changed, 28 insertions(+), 26 deletions(-) create mode 100644 src/UnresolvableQueryDynamicFromException.php delete mode 100644 src/UnresolvableQueryInvalidAfterSimulationException.php diff --git a/src/QueryReflection/QueryReflection.php b/src/QueryReflection/QueryReflection.php index 1f3f06e96..c4e0f5e17 100644 --- a/src/QueryReflection/QueryReflection.php +++ b/src/QueryReflection/QueryReflection.php @@ -32,6 +32,7 @@ use staabm\PHPStanDba\SchemaReflection\SchemaReflection; use staabm\PHPStanDba\SqlAst\ParserInference; use staabm\PHPStanDba\UnresolvableQueryException; +use staabm\PHPStanDba\UnresolvableQueryDynamicFromException; final class QueryReflection { @@ -291,13 +292,7 @@ public function resolveQueryStrings(Expr $queryExpr, Scope $scope): iterable $queryString = $this->resolveQueryExpr($queryExpr, $scope); if (null !== $queryString) { - $normalizedQuery = QuerySimulation::stripComments($this->normalizeQueryString($queryString)); - - // query simulation might lead in a invalid query, skip those - $error = $this->validateQueryString($normalizedQuery); - if ($error === null) { - yield $normalizedQuery; - } + yield QuerySimulation::stripComments($this->normalizeQueryString($queryString)); } } @@ -371,6 +366,13 @@ private function resolveQueryStringExpr(Expr $queryExpr, Scope $scope, bool $res $leftString = $this->resolveQueryStringExpr($left, $scope); $rightString = $this->resolveQueryStringExpr($right, $scope); + // queries with a dynamic FROM are not resolvable + if (QueryReflection::getRuntimeConfiguration()->isDebugEnabled()) { + if (str_ends_with(rtrim($leftString), 'FROM') && is_numeric(trim($rightString, '"\''))) { + throw new UnresolvableQueryDynamicFromException('Seems the query is too dynamic to be resolved by query simulation'); + } + } + if (null === $leftString || null === $rightString) { return null; } diff --git a/src/UnresolvableQueryDynamicFromException.php b/src/UnresolvableQueryDynamicFromException.php new file mode 100644 index 000000000..13b2cf8da --- /dev/null +++ b/src/UnresolvableQueryDynamicFromException.php @@ -0,0 +1,16 @@ + Date: Sat, 11 Mar 2023 09:16:10 +0100 Subject: [PATCH 11/11] fix --- src/QueryReflection/QueryReflection.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QueryReflection/QueryReflection.php b/src/QueryReflection/QueryReflection.php index c4e0f5e17..02c64800c 100644 --- a/src/QueryReflection/QueryReflection.php +++ b/src/QueryReflection/QueryReflection.php @@ -366,6 +366,10 @@ private function resolveQueryStringExpr(Expr $queryExpr, Scope $scope, bool $res $leftString = $this->resolveQueryStringExpr($left, $scope); $rightString = $this->resolveQueryStringExpr($right, $scope); + if (null === $leftString || null === $rightString) { + return null; + } + // queries with a dynamic FROM are not resolvable if (QueryReflection::getRuntimeConfiguration()->isDebugEnabled()) { if (str_ends_with(rtrim($leftString), 'FROM') && is_numeric(trim($rightString, '"\''))) { @@ -373,10 +377,6 @@ private function resolveQueryStringExpr(Expr $queryExpr, Scope $scope, bool $res } } - if (null === $leftString || null === $rightString) { - return null; - } - return $leftString . $rightString; }