Merge pull request #19698 from kamil-tekiela/Routines-enum

Introduce enum RoutineType
This commit is contained in:
Maurício Meneghini Fauth 2025-05-11 16:11:01 -03:00 committed by GitHub
commit fd4e7bc80b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 148 additions and 189 deletions

View File

@ -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

View File

@ -973,8 +973,8 @@
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_type']]]></code>
<code><![CDATA[$_POST['item_type']]]></code>
<code><![CDATA[$_REQUEST['item_name']]]></code>
<code><![CDATA[$_REQUEST['type'] ?? '']]></code>
</PossiblyInvalidArgument>
<PossiblyInvalidCast>
<code><![CDATA[$_GET['item_name']]]></code>
@ -3468,10 +3468,6 @@
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
<InvalidArgument>
<code><![CDATA[$itemParamDir]]></code>
<code><![CDATA[$itemParamName]]></code>
</InvalidArgument>
<InvalidArrayOffset>
<code><![CDATA[$retval['item_param_dir'][$key]]]></code>
</InvalidArrayOffset>
@ -3500,6 +3496,8 @@
<code><![CDATA[$value]]></code>
</MixedArgument>
<MixedArgumentTypeCoercion>
<code><![CDATA[$itemParamDir]]></code>
<code><![CDATA[$itemParamName]]></code>
<code><![CDATA[$options]]></code>
<code><![CDATA[$options]]></code>
<code><![CDATA[$retval['item_param_opts_num'][$idx]]]></code>
@ -3546,6 +3544,9 @@
</MixedArrayOffset>
<MixedAssignment>
<code><![CDATA[$i]]></code>
<code><![CDATA[$itemParamLength]]></code>
<code><![CDATA[$itemParamName]]></code>
<code><![CDATA[$itemParamType]]></code>
<code><![CDATA[$opt]]></code>
<code><![CDATA[$opt]]></code>
<code><![CDATA[$options[]]]></code>
@ -3565,7 +3566,6 @@
</MixedReturnTypeCoercion>
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['funcs'][$routine['item_param_name'][$i]]]]></code>
<code><![CDATA[$_POST['item_comment']]]></code>
<code><![CDATA[$_POST['item_isdeterministic']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
@ -3579,17 +3579,12 @@
<code><![CDATA[$_POST['item_returnopts_num']]]></code>
<code><![CDATA[$_POST['item_returnopts_text']]]></code>
<code><![CDATA[$_POST['item_type']]]></code>
<code><![CDATA[$itemDefiner]]></code>
<code><![CDATA[$itemName]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemType]]></code>
<code><![CDATA[$itemType]]></code>
</PossiblyInvalidArgument>
<PossiblyInvalidCast>
<code><![CDATA[$_POST['item_comment']]]></code>
<code><![CDATA[$_POST['item_isdeterministic']]]></code>
<code><![CDATA[$_POST['item_name']]]></code>
<code><![CDATA[$_POST['item_original_name']]]></code>
@ -3603,7 +3598,6 @@
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemReturnType]]></code>
<code><![CDATA[$itemType]]></code>
</PossiblyInvalidCast>
<PossiblyInvalidIterator>
<code><![CDATA[$retval['item_param_dir']]]></code>
@ -3611,10 +3605,10 @@
<PossiblyInvalidOperand>
<code><![CDATA[$_POST['item_original_type']]]></code>
<code><![CDATA[$_POST['item_returnlength']]]></code>
<code><![CDATA[$itemDefinition]]></code>
</PossiblyInvalidOperand>
<PossiblyNullArgument>
<code><![CDATA[$createRoutine]]></code>
<code><![CDATA[$itemType]]></code>
<code><![CDATA[$param->type->parameters]]></code>
<code><![CDATA[$routine['SPECIFIC_NAME']]]></code>
<code><![CDATA[$routine['SPECIFIC_NAME']]]></code>
@ -3648,20 +3642,12 @@
<code><![CDATA[empty($_POST['editor_process_edit'])]]></code>
<code><![CDATA[empty($_POST['funcs'][$routine['item_param_name'][$i]])]]></code>
<code><![CDATA[empty($_POST['item_adjust_privileges'])]]></code>
<code><![CDATA[empty($_POST['item_comment'])]]></code>
<code><![CDATA[empty($_POST['item_returnlength'])]]></code>
<code><![CDATA[empty($_POST['item_returnlength'])]]></code>
<code><![CDATA[empty($_POST['item_returnopts_num'])]]></code>
<code><![CDATA[empty($_POST['item_returnopts_text'])]]></code>
<code><![CDATA[empty($itemDefiner)]]></code>
<code><![CDATA[empty($itemDefinition)]]></code>
<code><![CDATA[empty($itemName)]]></code>
<code><![CDATA[empty($itemParamDir[$i])]]></code>
<code><![CDATA[empty($itemParamLength)]]></code>
<code><![CDATA[empty($itemParamName)]]></code>
<code><![CDATA[empty($itemParamName[$i])]]></code>
<code><![CDATA[empty($itemParamType)]]></code>
<code><![CDATA[empty($itemSecurityType)]]></code>
<code><![CDATA[empty($routine['DTD_IDENTIFIER'])]]></code>
</RiskyTruthyFalsyComparison>
</file>

View File

@ -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');

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Database;
enum RoutineType: string
{
case Function = 'FUNCTION';
case Procedure = 'PROCEDURE';
}

View File

@ -9,6 +9,7 @@ use PhpMyAdmin\Config;
use PhpMyAdmin\Current;
use PhpMyAdmin\Dbal\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
use PhpMyAdmin\Query\Generator as QueryGenerator;
use PhpMyAdmin\SqlParser\Parser;
@ -68,10 +69,13 @@ class Routines
/**
* Handle request to create or edit a routine
*/
public function handleRequestCreateOrEdit(UserPrivileges $userPrivileges, string $db): string
{
public function handleRequestCreateOrEdit(
UserPrivileges $userPrivileges,
string $db,
ServerRequest $request,
): string {
$sqlQuery = '';
$routineQuery = $this->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<int, string>
* @psalm-return list<non-empty-string>
*/
public static function getFunctionNames(DatabaseInterface $dbi, string $db): array
public static function getNames(DatabaseInterface $dbi, string $db, RoutineType $type): array
{
/** @psalm-var list<array{Db: string, Name: string, Type: string}> $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<int, string>
* @psalm-return list<non-empty-string>
*/
public static function getProcedureNames(DatabaseInterface $dbi, string $db): array
{
/** @psalm-var list<array{Db: string, Name: string, Type: string}> $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<non-empty-string> $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;
}

View File

@ -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) {

View File

@ -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"

View File

@ -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),
);
}

View File

@ -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) {

View File

@ -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;
}
/**

View File

@ -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();

View File

@ -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
' &lt;/pma:structure_schemas&gt;',
$result,
);
$dbiDummy->assertAllQueriesConsumed();
}
public function testExportFooter(): void

View File

@ -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`',