diff --git a/.phpstan-dba.cache b/.phpstan-dba.cache index 1c8fe36d9..0ee1a84e3 100644 --- a/.phpstan-dba.cache +++ b/.phpstan-dba.cache @@ -909,6 +909,47 @@ )), ), ), + 'SELECT adaid FROM ada LIMIT 1 OFFSET 1' => + array ( + 'error' => NULL, + 'result' => + array ( + 1 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + 'itemType' => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + ), + 'nextAutoIndex' => 0, + 'optionalKeys' => + array ( + ), + )), + ), + ), 'SELECT adaid FROM ada WHERE adaid = 1' => array ( 'error' => NULL, diff --git a/.phpunit-phpstan-dba.cache b/.phpunit-phpstan-dba.cache index 08ef578b8..6018e8ce3 100644 --- a/.phpunit-phpstan-dba.cache +++ b/.phpunit-phpstan-dba.cache @@ -24,6 +24,26 @@ array ( 'error' => NULL, ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET \'1\' + ' => + array ( + 'error' => NULL, + ), + ' + SELECT email, adaid + FROM ada + WHERE gesperrt = \'1\' + LIMIT \'1\' + OFFSET 1 + ' => + array ( + 'error' => NULL, + ), ' SELECT email, adaid FROM ada @@ -1009,6 +1029,47 @@ )), ), ), + 'SELECT adaid FROM ada LIMIT 1 OFFSET 1' => + array ( + 'error' => NULL, + 'result' => + array ( + 1 => + PHPStan\Type\Constant\ConstantArrayType::__set_state(array( + 'keyType' => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + 'itemType' => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + 'allArrays' => NULL, + 'keyTypes' => + array ( + 0 => + PHPStan\Type\Constant\ConstantStringType::__set_state(array( + 'value' => 'adaid', + 'isClassString' => false, + )), + ), + 'valueTypes' => + array ( + 0 => + PHPStan\Type\IntegerRangeType::__set_state(array( + 'min' => 0, + 'max' => 4294967295, + )), + ), + 'nextAutoIndex' => 0, + 'optionalKeys' => + array ( + ), + )), + ), + ), 'SELECT adaid FROM ada WHERE adaid IN (\'1\')' => array ( 'error' => NULL, diff --git a/src/QueryReflection/QuerySimulation.php b/src/QueryReflection/QuerySimulation.php index 49d82348e..639f58d69 100644 --- a/src/QueryReflection/QuerySimulation.php +++ b/src/QueryReflection/QuerySimulation.php @@ -4,6 +4,7 @@ namespace staabm\PHPStanDba\QueryReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\ConstantScalarType; @@ -113,6 +114,13 @@ private static function stripTraillingLimit(string $queryString): ?string { $queryString = rtrim($queryString, ';'); + // strip trailling OFFSET + $queryString = preg_replace('/(.*)OFFSET\s+["\']?\d+["\']?\s*$/i', '$1', $queryString); + + if (null === $queryString) { + throw new ShouldNotHappenException('Could not strip trailing offset from query'); + } + // XXX someday we will use a proper SQL parser, // which would also allow us to support even more complex expressions like SELECT .. LIMIT X, Y FOR UPDATE return preg_replace('/\s*LIMIT\s+["\']?\d+["\']?\s*(,\s*["\']?\d*["\']?)?\s*$/i', '', $queryString); diff --git a/tests/data/pdo.php b/tests/data/pdo.php index 937336f62..d3535f7f5 100644 --- a/tests/data/pdo.php +++ b/tests/data/pdo.php @@ -163,4 +163,11 @@ public function placeholderInData(PDO $pdo) $stmt = $pdo->query($query, PDO::FETCH_ASSOC); assertType('PDOStatement}>', $stmt); } + + public function offsetAfterLimit(PDO $pdo, int $limit, int $offset) + { + $query = 'SELECT adaid FROM ada LIMIT '.$limit.' OFFSET '.$offset; + $stmt = $pdo->query($query, PDO::FETCH_ASSOC); + assertType('PDOStatement}>', $stmt); + } } diff --git a/tests/data/syntax-error-in-prepared-statement.php b/tests/data/syntax-error-in-prepared-statement.php index 10e8ec8c7..b66720baf 100644 --- a/tests/data/syntax-error-in-prepared-statement.php +++ b/tests/data/syntax-error-in-prepared-statement.php @@ -208,4 +208,42 @@ public function noErrorInBug94(Connection $connection) $orderId = 15315351; $connection->preparedQuery($sql, ['orderId' => $orderId, 'productId' => $productId, 'supplierId' => $supplierId]); } + + public function noErrorOnBug175(Connection $connection, int $limit, int $offset) + { + $connection->preparedQuery(' + SELECT email, adaid + FROM ada + WHERE gesperrt = ? + LIMIT ? + OFFSET '.$offset.' + ', [1, $limit]); + + $connection->preparedQuery(' + SELECT email, adaid + FROM ada + WHERE gesperrt = ? + LIMIT ? + OFFSET '.((int) $offset).' + ', [1, $limit]); + } + + public function noErrorOnOffsetAfterLimit(Connection $connection, int $limit, int $offset) + { + $connection->preparedQuery(' + SELECT email, adaid + FROM ada + WHERE gesperrt = ? + LIMIT ? + OFFSET ? + ', [1, $limit, $offset]); + + $connection->preparedQuery(' + SELECT email, adaid + FROM ada + WHERE gesperrt = :gesperrt + LIMIT :limit + OFFSET :offset + ', [':gesperrt' => 1, ':limit' => $limit, ':offset' => $offset]); + } }