Use native MySQL paging

Signed-off-by: Kamil Tekiela <tekiela246@gmail.com>
This commit is contained in:
Kamil Tekiela 2025-04-30 17:59:38 +01:00 committed by Maurício Meneghini Fauth
parent b9726b7327
commit 8c62b1a1b5
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
6 changed files with 85 additions and 87 deletions

View File

@ -5205,7 +5205,7 @@ parameters:
Use dependency injection instead\.$#
'''
identifier: staticMethod.deprecated
count: 3
count: 2
path: src/Database/Routines.php
-
@ -5271,7 +5271,7 @@ parameters:
-
message: '#^Loose comparison via "\=\=" is not allowed\.$#'
identifier: equal.notAllowed
count: 3
count: 1
path: src/Database/Routines.php
-

View File

@ -3467,7 +3467,6 @@
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
<InvalidArgument>
<code><![CDATA[$itemParamDir]]></code>

View File

@ -25,8 +25,6 @@ use PhpMyAdmin\UserPrivilegesFactory;
use PhpMyAdmin\Util;
use function __;
use function array_slice;
use function count;
use function htmlentities;
use function htmlspecialchars;
use function in_array;
@ -457,8 +455,7 @@ final readonly class RoutinesController implements InvocableController
$type = null;
}
$items = Routines::getDetails($this->dbi, Current::$database, $type);
$totalNumRoutines = count($items);
$totalNumRoutines = Routines::getRoutineCount($this->dbi, Current::$database, $type);
$pageSize = $this->config->settings['MaxRoutineList'];
$pos = (int) $request->getParam('pos');
@ -481,7 +478,7 @@ final readonly class RoutinesController implements InvocableController
return $this->response->response();
}
$items = array_slice($items, $pos, $pageSize);
$items = Routines::getDetails($this->dbi, Current::$database, $type, limit: $pageSize, offset: $pos);
$isAjax = $request->isAjax() && empty($_REQUEST['ajax_page_request']);

View File

@ -20,9 +20,7 @@ use PhpMyAdmin\Util;
use function __;
use function _ngettext;
use function array_column;
use function array_merge;
use function array_multisort;
use function count;
use function explode;
use function htmlentities;
@ -42,7 +40,6 @@ use function str_starts_with;
use function stripos;
use const ENT_QUOTES;
use const SORT_ASC;
/**
* Functions for routine management.
@ -1203,35 +1200,17 @@ class Routines
string $db,
string|null $which = null,
string $name = '',
int $limit = 0,
int $offset = 0,
): array {
if (! Config::getInstance()->selectedServer['DisableIS']) {
$query = QueryGenerator::getInformationSchemaRoutinesRequest(
$dbi->quoteString($db),
in_array($which, ['FUNCTION', 'PROCEDURE'], true) ? $which : null,
$name === '' ? null : $dbi->quoteString($name),
);
$routines = $dbi->fetchResultSimple($query);
} else {
$routines = [];
if ($which === 'FUNCTION' || $which == null) {
$query = 'SHOW FUNCTION STATUS WHERE `Db` = ' . $dbi->quoteString($db);
if ($name !== '') {
$query .= ' AND `Name` = ' . $dbi->quoteString($name);
}
$routines = $dbi->fetchResultSimple($query);
}
if ($which === 'PROCEDURE' || $which == null) {
$query = 'SHOW PROCEDURE STATUS WHERE `Db` = ' . $dbi->quoteString($db);
if ($name !== '') {
$query .= ' AND `Name` = ' . $dbi->quoteString($name);
}
$routines = array_merge($routines, $dbi->fetchResultSimple($query));
}
}
$query = QueryGenerator::getInformationSchemaRoutinesRequest(
$dbi->quoteString($db),
in_array($which, ['FUNCTION', 'PROCEDURE'], true) ? $which : null,
$name === '' ? null : $dbi->quoteString($name),
$limit,
$offset,
);
$routines = $dbi->fetchResultSimple($query);
$ret = [];
/** @var array{Name:string, Type:string, Definer:string, DTD_IDENTIFIER:string|null} $routine */
@ -1244,13 +1223,19 @@ class Routines
);
}
// Sort results by name
$name = array_column($ret, 'name');
array_multisort($name, SORT_ASC, $ret);
return $ret;
}
public static function getRoutineCount(DatabaseInterface $dbi, string $db, string|null $which = null): int
{
$query = QueryGenerator::getInformationSchemaRoutinesCountRequest(
$dbi->quoteString($db),
in_array($which, ['FUNCTION', 'PROCEDURE'], true) ? $which : null,
);
return (int) $dbi->fetchValue($query);
}
public static function getFunctionDefinition(DatabaseInterface $dbi, string $db, string $name): string|null
{
$result = $dbi->fetchValue(

View File

@ -177,19 +177,13 @@ class Generator
string $quotedDbName,
string|null $routineType,
string|null $quotedRoutineName,
int $limit = 0,
int $offset = 0,
): string {
$query = 'SELECT'
. ' `ROUTINE_SCHEMA` AS `Db`,'
. ' `SPECIFIC_NAME` AS `Name`,'
. ' `ROUTINE_TYPE` AS `Type`,'
. ' `DEFINER` AS `Definer`,'
. ' `LAST_ALTERED` AS `Modified`,'
. ' `CREATED` AS `Created`,'
. ' `SECURITY_TYPE` AS `Security_type`,'
. ' `ROUTINE_COMMENT` AS `Comment`,'
. ' `CHARACTER_SET_CLIENT` AS `character_set_client`,'
. ' `COLLATION_CONNECTION` AS `collation_connection`,'
. ' `DATABASE_COLLATION` AS `Database Collation`,'
. ' `DTD_IDENTIFIER`'
. ' FROM `information_schema`.`ROUTINES`'
. ' WHERE `ROUTINE_SCHEMA` ' . Util::getCollateForIS()
@ -202,6 +196,36 @@ class Generator
$query .= ' AND `SPECIFIC_NAME` = ' . $quotedRoutineName;
}
$query .= ' ORDER BY `SPECIFIC_NAME`';
if ($limit > 0) {
$query .= ' LIMIT ' . $limit;
}
if ($offset > 0) {
$query .= ' OFFSET ' . $offset;
}
return $query;
}
public static function getInformationSchemaRoutinesCountRequest(
string $quotedDbName,
string|null $routineType,
string|null $quotedRoutineName = null,
): string {
$query = 'SELECT COUNT(*) AS `count`'
. ' FROM `information_schema`.`ROUTINES`'
. ' WHERE `ROUTINE_SCHEMA` ' . Util::getCollateForIS()
. ' = ' . $quotedDbName;
if ($routineType !== null) {
$query .= " AND `ROUTINE_TYPE` = '" . $routineType . "'";
}
if ($quotedRoutineName !== null) {
$query .= ' AND `SPECIFIC_NAME` = ' . $quotedRoutineName;
}
return $query;
}

View File

@ -39,20 +39,20 @@ final class RoutinesControllerTest extends AbstractTestCase
['Grants for definer@localhost'],
);
$dummyDbi->addResult(
'SHOW FUNCTION STATUS WHERE `Db` = \'test_db\'',
[['test_db', 'test_func', 'FUNCTION', 'definer@localhost']],
['Db', 'Name', 'Type', 'Definer'],
);
$dummyDbi->addResult(
'SHOW PROCEDURE STATUS WHERE `Db` = \'test_db\'',
[['test_db', 'test_proc', 'PROCEDURE', 'definer@localhost']],
['Db', 'Name', 'Type', 'Definer'],
"SELECT `SPECIFIC_NAME` AS `Name`, `ROUTINE_TYPE` AS `Type`, `DEFINER` AS `Definer`, `DTD_IDENTIFIER` FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` COLLATE utf8_bin = 'test_db' ORDER BY `SPECIFIC_NAME` LIMIT 250",
[['test_db', 'test_func', 'FUNCTION', 'definer@localhost', null], ['test_db', 'test_proc', 'PROCEDURE', 'definer@localhost', null]],
['Db', 'Name', 'Type', 'Definer', 'DTD_IDENTIFIER'],
);
$dummyDbi->addResult('SELECT @@lower_case_table_names', []);
$dummyDbi->addResult(
"SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA COLLATE utf8_bin='test_db' AND SPECIFIC_NAME='test_func' AND ROUTINE_TYPE='FUNCTION';",
[['definer@localhost']],
['DEFINER'],
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='CREATE ROUTINE'",
[['CREATE ROUTINE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='EXECUTE'",
[['EXECUTE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='CREATE ROUTINE'",
@ -64,35 +64,15 @@ final class RoutinesControllerTest extends AbstractTestCase
[['EXECUTE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult(
'SHOW CREATE FUNCTION `test_db`.`test_func`',
[['test_func', 'CREATE FUNCTION `test_func` (p INT) RETURNS int(11) BEGIN END']],
['Function', 'Create Function'],
);
$dummyDbi->addResult(
"SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA COLLATE utf8_bin='test_db' AND SPECIFIC_NAME='test_proc' AND ROUTINE_TYPE='PROCEDURE';",
[['definer@localhost']],
['DEFINER'],
);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='CREATE ROUTINE'",
[['CREATE ROUTINE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='EXECUTE'",
[['EXECUTE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult(
'SHOW CREATE PROCEDURE `test_db`.`test_proc`',
[['test_proc2', 'CREATE PROCEDURE `test_proc2` (p INT) BEGIN END']],
['Procedure', 'Create Procedure'],
);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='CREATE ROUTINE'",
[['CREATE ROUTINE']],
['PRIVILEGE_TYPE'],
"SELECT COUNT(*) AS `count` FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` COLLATE utf8_bin = 'test_db'",
[[2]],
['count'],
);
// phpcs:enable
@ -293,6 +273,8 @@ HTML;
// phpcs:enable
self::assertSame($expected, $actual);
$dummyDbi->assertAllQueriesConsumed();
$dummyDbi->assertAllSelectsConsumed();
}
public function testWithoutRoutines(): void
@ -312,13 +294,22 @@ HTML;
[['GRANT ALL PRIVILEGES ON *.* TO `definer`@`localhost`']],
['Grants for definer@localhost'],
);
$dummyDbi->addResult('SHOW FUNCTION STATUS WHERE `Db` = \'test_db\'', [], ['Db', 'Name', 'Type', 'Definer']);
$dummyDbi->addResult('SHOW PROCEDURE STATUS WHERE `Db` = \'test_db\'', [], ['Db', 'Name', 'Type', 'Definer']);
$dummyDbi->addResult(
"SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` WHERE GRANTEE='''definer''@''localhost''' AND PRIVILEGE_TYPE='CREATE ROUTINE'",
[['CREATE ROUTINE']],
['PRIVILEGE_TYPE'],
);
$dummyDbi->addResult('SELECT @@lower_case_table_names', []);
$dummyDbi->addResult(
"SELECT COUNT(*) AS `count` FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` COLLATE utf8_bin = 'test_db'",
[[1]],
['count'],
);
$dummyDbi->addResult(
"SELECT `SPECIFIC_NAME` AS `Name`, `ROUTINE_TYPE` AS `Type`, `DEFINER` AS `Definer`, `DTD_IDENTIFIER` FROM `information_schema`.`ROUTINES` WHERE `ROUTINE_SCHEMA` COLLATE utf8_bin = 'test_db' ORDER BY `SPECIFIC_NAME` LIMIT 250",
[],
['Db', 'Name', 'Type', 'Definer', 'DTD_IDENTIFIER'],
);
// phpcs:enable
$dbi = $this->createDatabaseInterface($dummyDbi);
@ -438,5 +429,7 @@ HTML;
// phpcs:enable
self::assertSame($expected, $actual);
$dummyDbi->assertAllQueriesConsumed();
$dummyDbi->assertAllSelectsConsumed();
}
}