From 9d0f6110d5ff27ffc8a8d8e14fab339d71d7a720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Thu, 28 Aug 2025 02:05:45 +0200 Subject: [PATCH] Fix row count with SQL_NO_CACHE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Krög --- composer.json | 1 + libraries/classes/Sql.php | 21 ++++++++++++++++++++- test/classes/SqlTest.php | 8 ++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e3a9e67271..cf37f6b2b5 100644 --- a/composer.json +++ b/composer.json @@ -78,6 +78,7 @@ "symfony/polyfill-ctype": "^1.17.0", "symfony/polyfill-mbstring": "^1.17.0", "symfony/polyfill-php80": "^1.16", + "symfony/polyfill-php84": "^1.33", "twig/twig": "^3.3.5", "webmozart/assert": "^1.10", "williamdes/mariadb-mysql-kbs": "^1.2" diff --git a/libraries/classes/Sql.php b/libraries/classes/Sql.php index ac8ab5a106..59546e2deb 100644 --- a/libraries/classes/Sql.php +++ b/libraries/classes/Sql.php @@ -21,9 +21,11 @@ use PhpMyAdmin\SqlParser\Utils\Query; use PhpMyAdmin\Utils\ForeignKey; use function __; +use function array_find_key; use function array_key_exists; use function array_keys; use function array_map; +use function assert; use function bin2hex; use function ceil; use function count; @@ -34,11 +36,13 @@ use function in_array; use function is_array; use function is_bool; use function is_object; +use function is_string; use function session_start; use function session_write_close; use function sprintf; use function str_contains; use function str_replace; +use function strtoupper; use function ucwords; /** @@ -745,6 +749,15 @@ class Sql /** @var SelectStatement $statement */ $statement = $analyzedSqlResults['statement']; + assert($statement->options !== null); + /** @var int|null $noCacheIndex */ + $noCacheIndex = array_find_key( + $statement->options->options, + /** @param mixed $value */ + static function ($value): bool { + return is_string($value) && strtoupper($value) === 'SQL_NO_CACHE'; + } + ); $changeOrder = $analyzedSqlResults['order'] !== false; $changeLimit = $analyzedSqlResults['limit'] !== false; $changeExpression = $analyzedSqlResults['is_group'] === false @@ -752,8 +765,14 @@ class Sql && $analyzedSqlResults['union'] === false && count($statement->expr) === 1; - if ($changeOrder || $changeLimit || $changeExpression) { + if ($changeOrder || $changeLimit || $changeExpression || $noCacheIndex !== null) { $statement = clone $statement; + // Remove SQL_NO_CACHE from subquery because it is not valid sql + if ($noCacheIndex !== null) { + assert($statement->options !== null); + $statement->options = clone $statement->options; + unset($statement->options->options[$noCacheIndex]); + } } // Remove ORDER BY to decrease unnecessary sorting time diff --git a/test/classes/SqlTest.php b/test/classes/SqlTest.php index a3dcd17694..54bd517385 100644 --- a/test/classes/SqlTest.php +++ b/test/classes/SqlTest.php @@ -538,6 +538,14 @@ class SqlTest extends AbstractTestCase false, 'SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2 ) as cnt', ], + [ + 'SELECT SQL_NO_CACHE * FROM t1 WHERE id <> 0', + ['max_rows' => -1, 'pos' => 0], + 25, + 100, + false, + 'SELECT COUNT(*) FROM (SELECT 1 FROM t1 WHERE id <> 0 ) as cnt', + ], ]; }