diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 70b834ebe1..631b18876b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2028,6 +2028,12 @@ parameters: count: 1 path: src/Controllers/Database/RoutinesController.php + - + message: '#^Parameter \#1 \$value of static method PhpMyAdmin\\Database\\RoutineType\:\:tryFrom\(\) expects int\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Controllers/Database/RoutinesController.php + - message: '#^Parameter \#2 \$type of method PhpMyAdmin\\Database\\Routines\:\:getDataFromName\(\) expects string, mixed given\.$#' identifier: argument.type @@ -2046,12 +2052,6 @@ parameters: count: 1 path: src/Controllers/Database/RoutinesController.php - - - message: '#^Parameter \#3 \$which of static method PhpMyAdmin\\Database\\Routines\:\:getDetails\(\) expects string\|null, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/Controllers/Database/RoutinesController.php - - message: '#^Parameter \#4 \$name of static method PhpMyAdmin\\Database\\Routines\:\:getDetails\(\) expects string, mixed given\.$#' identifier: argument.type @@ -5193,12 +5193,6 @@ parameters: count: 2 path: src/Database/Routines.php - - - message: '#^Binary operation "\.\=" between non\-falsy\-string and mixed results in an error\.$#' - identifier: assignOp.invalid - count: 1 - path: src/Database/Routines.php - - message: ''' #^Call to deprecated method getInstance\(\) of class PhpMyAdmin\\Config\: @@ -5259,7 +5253,7 @@ parameters: - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' identifier: empty.notAllowed - count: 21 + count: 13 path: src/Database/Routines.php - @@ -5292,12 +5286,6 @@ parameters: count: 1 path: src/Database/Routines.php - - - message: '#^Parameter \#1 \$haystack of function str_contains expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/Database/Routines.php - - message: '#^Parameter \#1 \$haystack of function stripos expects string, mixed given\.$#' identifier: argument.type @@ -5307,7 +5295,7 @@ parameters: - message: '#^Parameter \#1 \$identifier of static method PhpMyAdmin\\Util\:\:backquote\(\) expects string\|Stringable\|null, mixed given\.$#' identifier: argument.type - count: 9 + count: 8 path: src/Database/Routines.php - @@ -5325,7 +5313,7 @@ parameters: - message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:quoteString\(\) expects string, mixed given\.$#' identifier: argument.type - count: 7 + count: 6 path: src/Database/Routines.php - @@ -5337,7 +5325,7 @@ parameters: - message: '#^Parameter \#1 \$string of function htmlspecialchars expects string, mixed given\.$#' identifier: argument.type - count: 2 + count: 1 path: src/Database/Routines.php - @@ -5370,12 +5358,6 @@ parameters: count: 1 path: src/Database/Routines.php - - - message: '#^Parameter \#2 \$string of function explode expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/Database/Routines.php - - message: '#^Parameter \#2 \$subject of function preg_match expects string, mixed given\.$#' identifier: argument.type @@ -5413,7 +5395,7 @@ parameters: path: src/Database/Routines.php - - message: '#^Parameter \#7 \$itemType of method PhpMyAdmin\\Database\\Routines\:\:processParamsAndBuild\(\) expects string, mixed given\.$#' + message: '#^Parameter \#7 \$itemType of method PhpMyAdmin\\Database\\Routines\:\:processParamsAndBuild\(\) expects PhpMyAdmin\\Database\\RoutineType, PhpMyAdmin\\Database\\RoutineType\|null given\.$#' identifier: argument.type count: 1 path: src/Database/Routines.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 34e2e439d9..e461f30da8 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -973,8 +973,8 @@ - + @@ -3468,10 +3468,6 @@ - - - - @@ -3500,6 +3496,8 @@ + + @@ -3546,6 +3544,9 @@ + + + @@ -3565,7 +3566,6 @@ - @@ -3579,17 +3579,12 @@ - - - - - @@ -3603,7 +3598,6 @@ - @@ -3611,10 +3605,10 @@ - + type->parameters]]> @@ -3648,20 +3642,12 @@ - - - - - - - - diff --git a/src/Controllers/Database/RoutinesController.php b/src/Controllers/Database/RoutinesController.php index a0befa26bd..c788d76ac9 100644 --- a/src/Controllers/Database/RoutinesController.php +++ b/src/Controllers/Database/RoutinesController.php @@ -9,6 +9,7 @@ use PhpMyAdmin\Config; use PhpMyAdmin\Controllers\InvocableController; use PhpMyAdmin\Current; use PhpMyAdmin\Database\Routines; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\DbTableExists; use PhpMyAdmin\Html\Generator; @@ -55,7 +56,7 @@ final readonly class RoutinesController implements InvocableController { $this->response->addScriptFiles(['database/routines.js', 'sql.js']); - $type = $_REQUEST['type'] ?? null; + $type = RoutineType::tryFrom($_REQUEST['type'] ?? ''); $userPrivileges = $this->userPrivilegesFactory->getPrivileges(); @@ -104,7 +105,7 @@ final readonly class RoutinesController implements InvocableController } if (! empty($_POST['editor_process_add']) || ! empty($_POST['editor_process_edit'])) { - $output = $this->routines->handleRequestCreateOrEdit($userPrivileges, Current::$database); + $output = $this->routines->handleRequestCreateOrEdit($userPrivileges, Current::$database, $request); if ($request->isAjax()) { if (! (Current::$message instanceof Message && Current::$message->isSuccess())) { $this->response->setRequestStatus(false); @@ -116,7 +117,7 @@ final readonly class RoutinesController implements InvocableController $routines = Routines::getDetails( $this->dbi, Current::$database, - $_POST['item_type'], + RoutineType::tryFrom($request->getParsedBodyParamAsString('item_type', '')), $_POST['item_name'], ); $routine = $routines[0]; @@ -451,10 +452,6 @@ final readonly class RoutinesController implements InvocableController } } - if (! isset($type) || ! in_array($type, ['FUNCTION', 'PROCEDURE'], true)) { - $type = null; - } - $totalNumRoutines = Routines::getRoutineCount($this->dbi, Current::$database, $type); $pageSize = $this->config->settings['MaxRoutineList']; $pos = (int) $request->getParam('pos'); diff --git a/src/Database/RoutineType.php b/src/Database/RoutineType.php new file mode 100644 index 0000000000..8bba2c5f24 --- /dev/null +++ b/src/Database/RoutineType.php @@ -0,0 +1,11 @@ +getQueryFromRequest(); + $routineQuery = $this->getQueryFromRequest($request); // set by getQueryFromRequest() if ($this->errors === []) { @@ -627,7 +631,6 @@ class Routines * @param mixed[] $itemParamLength A length or not for the parameter * @param mixed[] $itemParamOpsText An optional charset for the parameter * @param mixed[] $itemParamOpsNum An optional parameter for a $itemParamType NUMBER - * @param string $itemType The item type (PROCEDURE/FUNCTION) * @param bool $warnedAboutLength A boolean that will be switched if a the length warning is given */ private function processParamsAndBuild( @@ -637,7 +640,7 @@ class Routines array $itemParamLength, array $itemParamOpsText, array $itemParamOpsNum, - string $itemType, + RoutineType $itemType, bool &$warnedAboutLength, ): string { $params = ''; @@ -650,14 +653,14 @@ class Routines } if ( - $itemType === 'PROCEDURE' + $itemType === RoutineType::Procedure && ! empty($itemParamDir[$i]) && in_array($itemParamDir[$i], $this->directions, true) ) { $params .= $itemParamDir[$i] . ' ' . Util::backquote($itemParamName[$i]) . ' ' . $itemParamType[$i]; - } elseif ($itemType === 'FUNCTION') { + } elseif ($itemType === RoutineType::Function) { $params .= Util::backquote($itemParamName[$i]) . ' ' . $itemParamType[$i]; } elseif (! $warnedAboutDir) { @@ -775,14 +778,14 @@ class Routines * * @return string The CREATE [ROUTINE | PROCEDURE] query. */ - public function getQueryFromRequest(): string + public function getQueryFromRequest(ServerRequest $request): string { - $itemType = $_POST['item_type'] ?? ''; - $itemDefiner = $_POST['item_definer'] ?? ''; - $itemName = $_POST['item_name'] ?? ''; + $itemType = RoutineType::tryFrom($request->getParsedBodyParamAsString('item_type', '')); + $itemDefiner = $request->getParsedBodyParamAsString('item_definer', ''); + $itemName = $request->getParsedBodyParamAsString('item_name', ''); $query = 'CREATE '; - if (! empty($itemDefiner)) { + if ($itemDefiner !== '') { if (str_contains($itemDefiner, '@')) { $arr = explode('@', $itemDefiner); @@ -804,16 +807,13 @@ class Routines } } - if ($itemType === 'FUNCTION' || $itemType === 'PROCEDURE') { - $query .= $itemType . ' '; + if ($itemType !== null) { + $query .= $itemType->value . ' '; } else { - $this->errors[] = sprintf( - __('Invalid routine type: "%s"'), - htmlspecialchars($itemType), - ); + $this->errors[] = __('Invalid routine type!'); } - if (! empty($itemName)) { + if ($itemName !== '') { $query .= Util::backquote($itemName); } else { $this->errors[] = __('You must provide a routine name!'); @@ -821,18 +821,18 @@ class Routines $warnedAboutLength = false; - $itemParamName = $_POST['item_param_name'] ?? ''; - $itemParamType = $_POST['item_param_type'] ?? ''; - $itemParamLength = $_POST['item_param_length'] ?? ''; - $itemParamDir = (array) ($_POST['item_param_dir'] ?? []); - $itemParamOpsText = (array) ($_POST['item_param_opts_text'] ?? []); - $itemParamOpsNum = (array) ($_POST['item_param_opts_num'] ?? []); + $itemParamName = $request->getParsedBodyParam('item_param_name', ''); + $itemParamType = $request->getParsedBodyParam('item_param_type', ''); + $itemParamLength = $request->getParsedBodyParam('item_param_length', ''); + $itemParamDir = (array) $request->getParsedBodyParam('item_param_dir', []); + $itemParamOpsText = (array) $request->getParsedBodyParam('item_param_opts_text', []); + $itemParamOpsNum = (array) $request->getParsedBodyParam('item_param_opts_num', []); $params = ''; if ( - ! empty($itemParamName) - && ! empty($itemParamType) - && ! empty($itemParamLength) + $itemParamName !== [] + && $itemParamType !== [] + && $itemParamLength !== [] && is_array($itemParamName) && is_array($itemParamType) && is_array($itemParamLength) @@ -850,34 +850,33 @@ class Routines } $query .= '(' . $params . ') '; - if ($itemType === 'FUNCTION') { + if ($itemType === RoutineType::Function) { $query = $this->processFunctionSpecificParameters($query, $warnedAboutLength); } - if (! empty($_POST['item_comment'])) { - $query .= 'COMMENT ' . $this->dbi->quoteString($_POST['item_comment']) . ' '; + $itemComment = $request->getParsedBodyParamAsString('item_comment', ''); + if ($itemComment !== '') { + $query .= 'COMMENT ' . $this->dbi->quoteString($itemComment) . ' '; } - if (isset($_POST['item_isdeterministic'])) { + if ($request->hasBodyParam('item_isdeterministic')) { $query .= 'DETERMINISTIC '; } else { $query .= 'NOT DETERMINISTIC '; } - $itemSqlDataAccess = $_POST['item_sqldataaccess'] ?? ''; + $itemSqlDataAccess = $request->getParsedBodyParamAsString('item_sqldataaccess', ''); if (in_array($itemSqlDataAccess, $this->sqlDataAccess, true)) { $query .= $itemSqlDataAccess . ' '; } - $itemSecurityType = $_POST['item_securitytype'] ?? ''; - if (! empty($itemSecurityType)) { - if ($itemSecurityType === 'DEFINER' || $itemSecurityType === 'INVOKER') { - $query .= 'SQL SECURITY ' . $itemSecurityType . ' '; - } + $itemSecurityType = $request->getParsedBodyParamAsString('item_securitytype', ''); + if ($itemSecurityType === 'DEFINER' || $itemSecurityType === 'INVOKER') { + $query .= 'SQL SECURITY ' . $itemSecurityType . ' '; } - $itemDefinition = $_POST['item_definition'] ?? ''; - if (! empty($itemDefinition)) { + $itemDefinition = $request->getParsedBodyParamAsString('item_definition', ''); + if ($itemDefinition !== '') { $query .= $itemDefinition; } else { $this->errors[] = __('You must provide a routine definition.'); @@ -1189,23 +1188,21 @@ class Routines * returns details about the PROCEDUREs or FUNCTIONs for a specific database * or details about a specific routine * - * @param string $db db name - * @param string|null $which PROCEDURE | FUNCTION or null for both - * @param string $name name of the routine (to fetch a specific routine) + * @param string $name name of the routine (to fetch a specific routine) * * @return Routine[] */ public static function getDetails( DatabaseInterface $dbi, string $db, - string|null $which = null, + RoutineType|null $which = null, string $name = '', int $limit = 0, int $offset = 0, ): array { $query = QueryGenerator::getInformationSchemaRoutinesRequest( $dbi->quoteString($db), - in_array($which, ['FUNCTION', 'PROCEDURE'], true) ? $which : null, + $which, $name === '' ? null : $dbi->quoteString($name), $limit, $offset, @@ -1226,11 +1223,11 @@ class Routines return $ret; } - public static function getRoutineCount(DatabaseInterface $dbi, string $db, string|null $which = null): int + public static function getRoutineCount(DatabaseInterface $dbi, string $db, RoutineType|null $which = null): int { $query = QueryGenerator::getInformationSchemaRoutinesCountRequest( $dbi->quoteString($db), - in_array($which, ['FUNCTION', 'PROCEDURE'], true) ? $which : null, + $which, ); return (int) $dbi->fetchValue($query); @@ -1260,38 +1257,14 @@ class Routines * @return array * @psalm-return list */ - public static function getFunctionNames(DatabaseInterface $dbi, string $db): array + public static function getNames(DatabaseInterface $dbi, string $db, RoutineType $type): array { - /** @psalm-var list $functions */ - $functions = $dbi->fetchResultSimple('SHOW FUNCTION STATUS;'); - $names = []; - foreach ($functions as $function) { - if ($function['Db'] !== $db || $function['Type'] !== 'FUNCTION' || $function['Name'] === '') { - continue; - } - - $names[] = $function['Name']; - } - - return $names; - } - - /** - * @return array - * @psalm-return list - */ - public static function getProcedureNames(DatabaseInterface $dbi, string $db): array - { - /** @psalm-var list $procedures */ - $procedures = $dbi->fetchResultSimple('SHOW PROCEDURE STATUS;'); - $names = []; - foreach ($procedures as $procedure) { - if ($procedure['Db'] !== $db || $procedure['Type'] !== 'PROCEDURE' || $procedure['Name'] === '') { - continue; - } - - $names[] = $procedure['Name']; - } + /** @var list $names */ + $names = $dbi->fetchSingleColumn( + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES' + . ' WHERE ROUTINE_SCHEMA = ' . $dbi->quoteString($db) + . " AND ROUTINE_TYPE = '" . $type->value . "' AND SPECIFIC_NAME != ''", + ); return $names; } diff --git a/src/Operations.php b/src/Operations.php index 8199b30f90..f456c4b9a4 100644 --- a/src/Operations.php +++ b/src/Operations.php @@ -7,6 +7,7 @@ namespace PhpMyAdmin; use PhpMyAdmin\ConfigStorage\Relation; use PhpMyAdmin\Database\Events; use PhpMyAdmin\Database\Routines; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Engines\Innodb; use PhpMyAdmin\Identifiers\DatabaseName; @@ -55,7 +56,7 @@ class Operations */ public function runProcedureAndFunctionDefinitions(string $db, DatabaseName $newDatabaseName): void { - foreach (Routines::getProcedureNames($this->dbi, $db) as $procedureName) { + foreach (Routines::getNames($this->dbi, $db, RoutineType::Procedure) as $procedureName) { $this->dbi->selectDb($db); $query = Routines::getProcedureDefinition($this->dbi, $db, $procedureName); if ($query === null) { @@ -68,7 +69,7 @@ class Operations $this->dbi->query($query); } - foreach (Routines::getFunctionNames($this->dbi, $db) as $functionName) { + foreach (Routines::getNames($this->dbi, $db, RoutineType::Function) as $functionName) { $this->dbi->selectDb($db); $query = Routines::getFunctionDefinition($this->dbi, $db, $functionName); if ($query === null) { diff --git a/src/Plugins/Export/ExportSql.php b/src/Plugins/Export/ExportSql.php index d7db94927a..f5fa3dc5c1 100644 --- a/src/Plugins/Export/ExportSql.php +++ b/src/Plugins/Export/ExportSql.php @@ -13,6 +13,7 @@ use PhpMyAdmin\Config; use PhpMyAdmin\Current; use PhpMyAdmin\Database\Events; use PhpMyAdmin\Database\Routines; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Dbal\ConnectionType; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Exceptions\ExportException; @@ -617,8 +618,8 @@ class ExportSql extends ExportPlugin $delimiter = '$$'; $dbi = DatabaseInterface::getInstance(); - $procedureNames = Routines::getProcedureNames($dbi, $db); - $functionNames = Routines::getFunctionNames($dbi, $db); + $procedureNames = Routines::getNames($dbi, $db, RoutineType::Procedure); + $functionNames = Routines::getNames($dbi, $db, RoutineType::Function); if ($procedureNames || $functionNames) { $text .= "\n" diff --git a/src/Plugins/Export/ExportXml.php b/src/Plugins/Export/ExportXml.php index 814dce0728..8411a6e5d7 100644 --- a/src/Plugins/Export/ExportXml.php +++ b/src/Plugins/Export/ExportXml.php @@ -9,6 +9,7 @@ use PhpMyAdmin\Config; use PhpMyAdmin\Current; use PhpMyAdmin\Database\Events; use PhpMyAdmin\Database\Routines; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Dbal\ConnectionType; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Export\Export; @@ -307,7 +308,7 @@ class ExportXml extends ExportPlugin $head .= $this->exportDefinitions( Current::$database, 'function', - Routines::getFunctionNames($dbi, Current::$database), + Routines::getNames($dbi, Current::$database, RoutineType::Function), ); } @@ -315,7 +316,7 @@ class ExportXml extends ExportPlugin $head .= $this->exportDefinitions( Current::$database, 'procedure', - Routines::getProcedureNames($dbi, Current::$database), + Routines::getNames($dbi, Current::$database, RoutineType::Procedure), ); } diff --git a/src/Query/Generator.php b/src/Query/Generator.php index a22ca01995..090993e590 100644 --- a/src/Query/Generator.php +++ b/src/Query/Generator.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Query; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Triggers\Trigger; use PhpMyAdmin\Util; @@ -175,7 +176,7 @@ class Generator public static function getInformationSchemaRoutinesRequest( string $quotedDbName, - string|null $routineType, + RoutineType|null $routineType, string|null $quotedRoutineName, int $limit = 0, int $offset = 0, @@ -189,7 +190,7 @@ class Generator . ' WHERE `ROUTINE_SCHEMA` ' . Util::getCollateForIS() . ' = ' . $quotedDbName; if ($routineType !== null) { - $query .= " AND `ROUTINE_TYPE` = '" . $routineType . "'"; + $query .= " AND `ROUTINE_TYPE` = '" . $routineType->value . "'"; } if ($quotedRoutineName !== null) { @@ -211,7 +212,7 @@ class Generator public static function getInformationSchemaRoutinesCountRequest( string $quotedDbName, - string|null $routineType, + RoutineType|null $routineType, string|null $quotedRoutineName = null, ): string { $query = 'SELECT COUNT(*) AS `count`' @@ -219,7 +220,7 @@ class Generator . ' WHERE `ROUTINE_SCHEMA` ' . Util::getCollateForIS() . ' = ' . $quotedDbName; if ($routineType !== null) { - $query .= " AND `ROUTINE_TYPE` = '" . $routineType . "'"; + $query .= " AND `ROUTINE_TYPE` = '" . $routineType->value . "'"; } if ($quotedRoutineName !== null) { diff --git a/src/Server/Privileges.php b/src/Server/Privileges.php index b6d107895d..953bfdd180 100644 --- a/src/Server/Privileges.php +++ b/src/Server/Privileges.php @@ -3082,16 +3082,13 @@ class Privileges */ public function getRoutineType(string $dbname, string $routineName): string { - $routineData = Routines::getDetails($this->dbi, $dbname); - $routineName = mb_strtolower($routineName); + $routineData = Routines::getDetails($this->dbi, $dbname, name: $routineName); - foreach ($routineData as $routine) { - if (mb_strtolower($routine->name) === $routineName) { - return $routine->type; - } + if ($routineData === []) { + return ''; } - return ''; + return $routineData[0]->type; } /** diff --git a/tests/unit/Database/RoutinesTest.php b/tests/unit/Database/RoutinesTest.php index 10094f666f..93dae444f5 100644 --- a/tests/unit/Database/RoutinesTest.php +++ b/tests/unit/Database/RoutinesTest.php @@ -7,8 +7,10 @@ namespace PhpMyAdmin\Tests\Database; use PhpMyAdmin\Config; use PhpMyAdmin\Current; use PhpMyAdmin\Database\Routines; +use PhpMyAdmin\Database\RoutineType; use PhpMyAdmin\Dbal\ConnectionType; use PhpMyAdmin\Dbal\DatabaseInterface; +use PhpMyAdmin\Http\Factory\ServerRequestFactory; use PhpMyAdmin\Tests\AbstractTestCase; use PhpMyAdmin\Types; use PHPUnit\Framework\Attributes\CoversClass; @@ -253,7 +255,9 @@ class RoutinesTest extends AbstractTestCase unset($_POST); $_POST = $request; - self::assertSame($query, $routines->getQueryFromRequest()); + $request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/') + ->withParsedBody($request); + self::assertSame($query, $routines->getQueryFromRequest($request)); self::assertSame($numErr, $routines->getErrorCount()); // reset @@ -476,18 +480,16 @@ class RoutinesTest extends AbstractTestCase { $dbiDummy = $this->createDbiDummy(); $dbiDummy->addResult( - 'SHOW FUNCTION STATUS;', + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'FUNCTION' AND SPECIFIC_NAME != ''", [ - ['db_test', 'test_func', 'FUNCTION'], - ['test_db', 'test_func1', 'FUNCTION'], - ['test_db', '', 'FUNCTION'], - ['test_db', 'test_func2', 'FUNCTION'], - ['test_db', 'test_func', 'PROCEDURE'], + ['test_func1'], + ['test_func2'], ], - ['Db', 'Name', 'Type'], + ['Name'], ); - $names = Routines::getFunctionNames($this->createDatabaseInterface($dbiDummy), 'test_db'); + $names = Routines::getNames($this->createDatabaseInterface($dbiDummy), 'test_db', RoutineType::Function); self::assertSame(['test_func1', 'test_func2'], $names); $dbiDummy->assertAllQueriesConsumed(); @@ -497,12 +499,13 @@ class RoutinesTest extends AbstractTestCase { $dbiDummy = $this->createDbiDummy(); $dbiDummy->addResult( - 'SHOW FUNCTION STATUS;', - [['db_test', 'test_func', 'FUNCTION'], ['test_db', '', 'FUNCTION'], ['test_db', 'test_func', 'PROCEDURE']], + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'FUNCTION' AND SPECIFIC_NAME != ''", + [], ['Db', 'Name', 'Type'], ); - $names = Routines::getFunctionNames($this->createDatabaseInterface($dbiDummy), 'test_db'); + $names = Routines::getNames($this->createDatabaseInterface($dbiDummy), 'test_db', RoutineType::Function); self::assertSame([], $names); $dbiDummy->assertAllQueriesConsumed(); @@ -512,18 +515,16 @@ class RoutinesTest extends AbstractTestCase { $dbiDummy = $this->createDbiDummy(); $dbiDummy->addResult( - 'SHOW PROCEDURE STATUS;', + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'PROCEDURE' AND SPECIFIC_NAME != ''", [ - ['db_test', 'test_proc', 'PROCEDURE'], - ['test_db', 'test_proc1', 'PROCEDURE'], - ['test_db', '', 'PROCEDURE'], - ['test_db', 'test_proc2', 'PROCEDURE'], - ['test_db', 'test_proc', 'FUNCTION'], + ['test_proc1', 'PROCEDURE'], + ['test_proc2', 'PROCEDURE'], ], - ['Db', 'Name', 'Type'], + ['Name'], ); - $names = Routines::getProcedureNames($this->createDatabaseInterface($dbiDummy), 'test_db'); + $names = Routines::getNames($this->createDatabaseInterface($dbiDummy), 'test_db', RoutineType::Procedure); self::assertSame(['test_proc1', 'test_proc2'], $names); $dbiDummy->assertAllQueriesConsumed(); @@ -533,16 +534,13 @@ class RoutinesTest extends AbstractTestCase { $dbiDummy = $this->createDbiDummy(); $dbiDummy->addResult( - 'SHOW PROCEDURE STATUS;', - [ - ['db_test', 'test_proc', 'PROCEDURE'], - ['test_db', '', 'PROCEDURE'], - ['test_db', 'test_proc', 'FUNCTION'], - ], + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'PROCEDURE' AND SPECIFIC_NAME != ''", + [], ['Db', 'Name', 'Type'], ); - $names = Routines::getProcedureNames($this->createDatabaseInterface($dbiDummy), 'test_db'); + $names = Routines::getNames($this->createDatabaseInterface($dbiDummy), 'test_db', RoutineType::Procedure); self::assertSame([], $names); $dbiDummy->assertAllQueriesConsumed(); diff --git a/tests/unit/Plugins/Export/ExportXmlTest.php b/tests/unit/Plugins/Export/ExportXmlTest.php index 2fcebe2cee..da7ac09aa6 100644 --- a/tests/unit/Plugins/Export/ExportXmlTest.php +++ b/tests/unit/Plugins/Export/ExportXmlTest.php @@ -179,8 +179,8 @@ class ExportXmlTest extends AbstractTestCase $config->selectedServer['DisableIS'] = false; Current::$database = 'd<"b'; - $functions = [['d<"b', 'fn', 'FUNCTION']]; - $procedures = [['d<"b', 'pr', 'PROCEDURE']]; + $functions = [['fn']]; + $procedures = [['pr']]; $dbiDummy = $this->createDbiDummy(); $dbi = $this->createDatabaseInterface($dbiDummy); @@ -191,8 +191,18 @@ class ExportXmlTest extends AbstractTestCase [['utf-8', 'utf8_general_ci']], ['DEFAULT_CHARACTER_SET_NAME', 'DEFAULT_COLLATION_NAME'], ); - $dbiDummy->addResult('SHOW FUNCTION STATUS;', $functions, ['Db', 'Name', 'Type']); - $dbiDummy->addResult('SHOW PROCEDURE STATUS;', $procedures, ['Db', 'Name', 'Type']); + $dbiDummy->addResult( + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'd<\\\"b' AND ROUTINE_TYPE = 'FUNCTION' AND SPECIFIC_NAME != ''", + $functions, + ['Name'], + ); + $dbiDummy->addResult( + 'SELECT SPECIFIC_NAME FROM information_schema.ROUTINES ' + . "WHERE ROUTINE_SCHEMA = 'd<\\\"b' AND ROUTINE_TYPE = 'PROCEDURE' AND SPECIFIC_NAME != ''", + $procedures, + ['Name'], + ); $dbiDummy->addResult('SHOW CREATE TABLE `d<"b`.`table`', [['table', '"tbl"']]); $dbiDummy->addResult( 'SELECT 1 FROM information_schema.VIEWS WHERE TABLE_SCHEMA = \'d<\"b\' AND TABLE_NAME = \'table\'', @@ -327,6 +337,7 @@ class ExportXmlTest extends AbstractTestCase ' </pma:structure_schemas>', $result, ); + $dbiDummy->assertAllQueriesConsumed(); } public function testExportFooter(): void diff --git a/tests/unit/Stubs/DbiDummy.php b/tests/unit/Stubs/DbiDummy.php index c75f01523f..15ff21fc0a 100644 --- a/tests/unit/Stubs/DbiDummy.php +++ b/tests/unit/Stubs/DbiDummy.php @@ -1780,14 +1780,14 @@ class DbiDummy implements DbiExtension ], ], [ - 'query' => 'SHOW PROCEDURE STATUS;', - 'columns' => ['Db', 'Name', 'Type'], - 'result' => [['test_db', 'test_proc1', 'PROCEDURE'], ['test_db', 'test_proc2', 'PROCEDURE']], + 'query' => "SELECT SPECIFIC_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'PROCEDURE' AND SPECIFIC_NAME != ''", + 'columns' => ['Name'], + 'result' => [['test_proc1'], ['test_proc2']], ], [ - 'query' => 'SHOW FUNCTION STATUS;', - 'columns' => ['Db', 'Name', 'Type'], - 'result' => [['test_db', 'test_func', 'FUNCTION']], + 'query' => "SELECT SPECIFIC_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = 'test_db' AND ROUTINE_TYPE = 'FUNCTION' AND SPECIFIC_NAME != ''", + 'columns' => ['Name'], + 'result' => [['test_func']], ], [ 'query' => 'SHOW CREATE PROCEDURE `test_db`.`test_proc1`',