Merge pull request #18872 from kamil-tekiela/Refactor-Indexes-doSaveData

New implementation for Indexes
This commit is contained in:
Maurício Meneghini Fauth 2024-01-01 21:58:05 -03:00 committed by GitHub
commit ac0e478057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 404 additions and 447 deletions

View File

@ -193,7 +193,7 @@ return [
],
'table_indexes' => [
'class' => Indexes::class,
'arguments' => ['$response' => '@response', '$template' => '@template', '$dbi' => '@dbi'],
'arguments' => ['$dbi' => '@dbi'],
],
'table_maintenance' => ['class' => PhpMyAdmin\Table\Maintenance::class, 'arguments' => ['$dbi' => '@dbi']],
'table_search' => ['class' => Search::class, 'arguments' => ['$dbi' => '@dbi']],

View File

@ -4926,12 +4926,12 @@ parameters:
path: src/Controllers/Table/IndexRenameController.php
-
message: "#^Parameter \\#1 \\$params of static method PhpMyAdmin\\\\Url\\:\\:getCommon\\(\\) expects array\\<string, bool\\|int\\|string\\>, mixed given\\.$#"
message: "#^Parameter \\#1 \\$oldIndexName of method PhpMyAdmin\\\\Table\\\\Indexes\\:\\:getSqlQueryForRename\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Controllers/Table/IndexRenameController.php
-
message: "#^Parameter \\#7 \\$oldIndexName of method PhpMyAdmin\\\\Table\\\\Indexes\\:\\:doSaveData\\(\\) expects string, mixed given\\.$#"
message: "#^Parameter \\#1 \\$params of static method PhpMyAdmin\\\\Url\\:\\:getCommon\\(\\) expects array\\<string, bool\\|int\\|string\\>, mixed given\\.$#"
count: 1
path: src/Controllers/Table/IndexRenameController.php
@ -4955,6 +4955,11 @@ parameters:
count: 1
path: src/Controllers/Table/IndexesController.php
-
message: "#^Parameter \\#1 \\$oldIndexName of method PhpMyAdmin\\\\Table\\\\Indexes\\:\\:getSqlQueryForIndexCreateOrEdit\\(\\) expects string\\|null, mixed given\\.$#"
count: 1
path: src/Controllers/Table/IndexesController.php
-
message: "#^Parameter \\#1 \\$params of static method PhpMyAdmin\\\\Url\\:\\:getCommon\\(\\) expects array\\<string, bool\\|int\\|string\\>, mixed given\\.$#"
count: 1
@ -15165,6 +15170,11 @@ parameters:
count: 1
path: src/Table/Indexes.php
-
message: "#^Only booleans are allowed in a negated boolean, int\\|null given\\.$#"
count: 1
path: src/Table/Indexes.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 2
@ -15367,7 +15377,7 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 18
count: 17
path: src/Table/Table.php
-
@ -15437,7 +15447,7 @@ parameters:
-
message: "#^Only booleans are allowed in a negated boolean, int\\|null given\\.$#"
count: 2
count: 1
path: src/Table/Table.php
-
@ -15477,7 +15487,7 @@ parameters:
-
message: "#^Parameter \\#1 \\$identifier of static method PhpMyAdmin\\\\Util\\:\\:backquote\\(\\) expects string\\|Stringable\\|null, mixed given\\.$#"
count: 7
count: 6
path: src/Table/Table.php
-
@ -17895,26 +17905,6 @@ parameters:
count: 1
path: tests/classes/Stubs/ResponseRenderer.php
-
message: "#^Call to an undefined method PhpMyAdmin\\\\DatabaseInterface\\:\\:expects\\(\\)\\.$#"
count: 1
path: tests/classes/Table/IndexesTest.php
-
message: "#^Cannot call method method\\(\\) on mixed\\.$#"
count: 1
path: tests/classes/Table/IndexesTest.php
-
message: "#^Cannot call method willReturn\\(\\) on mixed\\.$#"
count: 1
path: tests/classes/Table/IndexesTest.php
-
message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, mixed given\\.$#"
count: 1
path: tests/classes/Table/IndexesTest.php
-
message: "#^Call to an undefined method PhpMyAdmin\\\\DatabaseInterface\\:\\:expects\\(\\)\\.$#"
count: 4
@ -17925,11 +17915,6 @@ parameters:
count: 1
path: tests/classes/Table/TableTest.php
-
message: "#^Cannot access offset 'Key_name' on mixed\\.$#"
count: 1
path: tests/classes/Table/TableTest.php
-
message: "#^Cannot access offset 'SCHEMA_TABLES' on mixed\\.$#"
count: 1

View File

@ -3568,20 +3568,15 @@
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
</InvalidArrayOffset>
<MixedArgument>
<code>$indexName</code>
<code>$indexName</code>
<code>$oldIndexName</code>
</MixedArgument>
<MixedAssignment>
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
<code>$indexName</code>
<code>$oldIndexName</code>
</MixedAssignment>
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['index']['Key_name']]]></code>
<code>$oldIndex</code>
</PossiblyInvalidArgument>
<PossiblyInvalidCast>
<code><![CDATA[$_POST['index']['Key_name']]]></code>
<code>$oldIndex</code>
</PossiblyInvalidCast>
</file>
<file src="src/Controllers/Table/IndexesController.php">
<DeprecatedMethod>
@ -3601,6 +3596,7 @@
</MixedAssignment>
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['columns']]]></code>
<code>$oldIndex</code>
</PossiblyInvalidArgument>
<PossiblyInvalidArrayOffset>
<code><![CDATA[$_POST['index']['Index_choice']]]></code>
@ -11466,6 +11462,11 @@
<code><![CDATA[$columnMeta['DefaultValue']]]></code>
</PossiblyUndefinedArrayOffset>
</file>
<file src="src/Table/Indexes.php">
<PossiblyNullOperand>
<code><![CDATA[$column->getSubPart()]]></code>
</PossiblyNullOperand>
</file>
<file src="src/Table/Search.php">
<MixedArgument>
<code><![CDATA[$_POST['criteriaColumnNames'][$columnIndex]]]></code>
@ -11736,7 +11737,6 @@
<PossiblyInvalidArgument>
<code><![CDATA[$_POST['constraint_name'][$masterFieldMd5]]]></code>
<code><![CDATA[$fields[$column]['expr']]]></code>
<code>$oldIndex</code>
<code><![CDATA[$options['expr']]]></code>
</PossiblyInvalidArgument>
<PossiblyInvalidArrayOffset>
@ -11776,7 +11776,6 @@
<code><![CDATA[$statement->altered]]></code>
</PossiblyNullIterator>
<PossiblyNullOperand>
<code><![CDATA[$column->getSubPart()]]></code>
<code><![CDATA[Generator::mysqlDie($tmpErrorCreate, $createQuery, false, '', false)]]></code>
<code><![CDATA[Generator::mysqlDie($tmpErrorDrop, $dropQuery, false, '', false)]]></code>
</PossiblyNullOperand>
@ -11787,9 +11786,6 @@
<PossiblyUnusedReturnValue>
<code>true|Message</code>
</PossiblyUnusedReturnValue>
<ReferenceConstraintViolation>
<code>return $sqlQuery;</code>
</ReferenceConstraintViolation>
<UnsupportedPropertyReferenceUsage>
<code><![CDATA[$this->uiprefs =& $_SESSION['tmpval']['table_uiprefs'][$serverId][$this->dbName][$this->name]]]></code>
</UnsupportedPropertyReferenceUsage>
@ -15256,6 +15252,9 @@
<MixedAssignment>
<code>$value</code>
</MixedAssignment>
<PossiblyUnusedMethod>
<code>clear</code>
</PossiblyUnusedMethod>
</file>
<file src="tests/classes/SystemDatabaseTest.php">
<DeprecatedMethod>
@ -15270,22 +15269,6 @@
<code>providerColumnMetaDefault</code>
</PossiblyUnusedMethod>
</file>
<file src="tests/classes/Table/IndexesTest.php">
<DeprecatedMethod>
<code>Config::getInstance()</code>
<code>DatabaseInterface::getInstance()</code>
</DeprecatedMethod>
<MixedArgument>
<code><![CDATA[$jsonArray['sql_data']]]></code>
</MixedArgument>
<MixedMethodCall>
<code>method</code>
<code>willReturn</code>
</MixedMethodCall>
<UndefinedMethod>
<code>expects</code>
</UndefinedMethod>
</file>
<file src="tests/classes/Table/Maintenance/MessageTest.php">
<PossiblyUnusedMethod>
<code>providerForTestFromArray</code>
@ -15319,8 +15302,6 @@
<code>DatabaseInterface::getInstance()</code>
<code>DatabaseInterface::getInstance()</code>
<code>DatabaseInterface::getInstance()</code>
<code>DatabaseInterface::getInstance()</code>
<code>DatabaseInterface::getInstance()</code>
</DeprecatedMethod>
<InvalidArrayOffset>
<code><![CDATA[$GLOBALS['sql_drop_table']]]></code>

View File

@ -15,7 +15,7 @@
</div>
<input type="text"
name="index[Key_name]"
name="index"
id="input_index_name"
size="25"
maxlength="64"

View File

@ -9,6 +9,7 @@ use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
@ -21,7 +22,6 @@ use PhpMyAdmin\Url;
use PhpMyAdmin\Util;
use function __;
use function is_array;
final class IndexRenameController extends AbstractController
{
@ -40,95 +40,110 @@ final class IndexRenameController extends AbstractController
$GLOBALS['urlParams'] ??= null;
$GLOBALS['errorUrl'] ??= null;
if (! isset($_POST['create_edit_table'])) {
if (! $this->checkParameters(['db', 'table'])) {
return;
}
$GLOBALS['urlParams'] = ['db' => Current::$database, 'table' => Current::$table];
$GLOBALS['errorUrl'] = Util::getScriptNameForOption(
Config::getInstance()->settings['DefaultTabTable'],
'table',
);
$GLOBALS['errorUrl'] .= Url::getCommon($GLOBALS['urlParams'], '&');
$databaseName = DatabaseName::tryFrom($request->getParam('db'));
if ($databaseName === null || ! $this->dbTableExists->selectDatabase($databaseName)) {
if ($request->isAjax()) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error(__('No databases selected.')));
return;
}
$this->redirect('/', ['reload' => true, 'message' => __('No databases selected.')]);
return;
}
$tableName = TableName::tryFrom($request->getParam('table'));
if ($tableName === null || ! $this->dbTableExists->hasTable($databaseName, $tableName)) {
if ($request->isAjax()) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error(__('No table selected.')));
return;
}
$this->redirect('/', ['reload' => true, 'message' => __('No table selected.')]);
return;
}
if (! $this->checkParameters(['db', 'table'])) {
return;
}
if (isset($_POST['index'])) {
if (is_array($_POST['index'])) {
// coming already from form
$oldIndex = is_array($_POST['old_index']) ? $_POST['old_index']['Key_name'] : $_POST['old_index'];
$index = clone $this->dbi->getTable(Current::$database, Current::$table)->getIndex($oldIndex);
$index->setName($_POST['index']['Key_name']);
} else {
$index = $this->dbi->getTable(Current::$database, Current::$table)->getIndex($_POST['index']);
$GLOBALS['urlParams'] = ['db' => Current::$database, 'table' => Current::$table];
$GLOBALS['errorUrl'] = Util::getScriptNameForOption(
Config::getInstance()->settings['DefaultTabTable'],
'table',
);
$GLOBALS['errorUrl'] .= Url::getCommon($GLOBALS['urlParams'], '&');
$databaseName = DatabaseName::tryFrom($request->getParam('db'));
if ($databaseName === null || ! $this->dbTableExists->selectDatabase($databaseName)) {
if ($request->isAjax()) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error(__('No databases selected.')));
return;
}
} else {
$index = new Index();
$this->redirect('/', ['reload' => true, 'message' => __('No databases selected.')]);
return;
}
if (isset($_POST['do_save_data'])) {
$oldIndexName = $request->getParsedBodyParam('old_index', '');
$this->indexes->doSaveData(
$request,
$index,
true,
Current::$database,
Current::$table,
$request->hasBodyParam('preview_sql'),
$oldIndexName,
$tableName = TableName::tryFrom($request->getParam('table'));
if ($tableName === null || ! $this->dbTableExists->hasTable($databaseName, $tableName)) {
if ($request->isAjax()) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', Message::error(__('No table selected.')));
return;
}
$this->redirect('/', ['reload' => true, 'message' => __('No table selected.')]);
return;
}
$oldIndexName = $request->getParsedBodyParam('old_index');
$indexName = $request->getParsedBodyParam('index');
if ($oldIndexName === null) {
$index = $this->dbi->getTable($databaseName->getName(), $tableName->getName())->getIndex($indexName);
$formParams = [
'db' => $databaseName->getName(),
'table' => $tableName->getName(),
'old_index' => $index->getName(),
];
$this->render('table/index_rename_form', ['index' => $index, 'form_params' => $formParams]);
return;
}
// coming already from form
$index = $this->dbi->getTable($databaseName->getName(), $tableName->getName())->getIndex($oldIndexName);
$index->setName($indexName);
$previewSql = $request->hasBodyParam('preview_sql');
$sqlQuery = $this->indexes->getSqlQueryForRename(
$oldIndexName,
$index,
$databaseName->getName(),
$tableName->getName(),
);
if ($previewSql) {
$this->response->addJSON(
'sql_data',
$this->template->render('preview_sql', ['query_data' => $sqlQuery]),
);
return;
}
$this->displayRenameForm($index);
}
$logicError = $this->indexes->getError();
if ($logicError instanceof Message) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $logicError);
/**
* Display the rename form to rename an index
*
* @param Index $index An Index instance.
*/
private function displayRenameForm(Index $index): void
{
$this->dbi->selectDb(Current::$database);
$formParams = ['db' => Current::$database, 'table' => Current::$table];
if (isset($_POST['old_index'])) {
$formParams['old_index'] = $_POST['old_index'];
} elseif (isset($_POST['index'])) {
$formParams['old_index'] = $_POST['index'];
return;
}
$this->render('table/index_rename_form', ['index' => $index, 'form_params' => $formParams]);
$this->dbi->query($sqlQuery);
$message = Message::success(__('Table %1$s has been altered successfully.'));
$message->addParam($tableName->getName());
$this->response->addJSON(
'message',
Generator::getMessage($message, $sqlQuery, 'success'),
);
$indexes = Index::getFromTable($this->dbi, $tableName->getName(), $databaseName->getName());
$indexesDuplicates = Index::findDuplicates($tableName->getName(), $databaseName->getName());
$this->response->addJSON(
'index_table',
$this->template->render('indexes', [
'url_params' => ['db' => $databaseName->getName(), 'table' => $tableName->getName()],
'indexes' => $indexes,
'indexes_duplicates' => $indexesDuplicates,
]),
);
}
}

View File

@ -5,10 +5,12 @@ declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Table;
use PhpMyAdmin\Config;
use PhpMyAdmin\Container\ContainerBuilder;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
@ -100,15 +102,69 @@ class IndexesController extends AbstractController
}
if (isset($_POST['do_save_data'])) {
$this->indexes->doSaveData(
$request,
$previewSql = $request->hasBodyParam('preview_sql');
if (isset($_POST['old_index'])) {
$oldIndex = is_array($_POST['old_index']) ? $_POST['old_index']['Key_name'] : $_POST['old_index'];
} else {
$oldIndex = null;
}
$sqlQuery = $this->indexes->getSqlQueryForIndexCreateOrEdit(
$oldIndex,
$index,
false,
Current::$database,
Current::$table,
$request->hasBodyParam('preview_sql'),
);
// If there is a request for SQL previewing.
if ($previewSql) {
$this->response->addJSON(
'sql_data',
$this->template->render('preview_sql', ['query_data' => $sqlQuery]),
);
return;
}
$logicError = $this->indexes->getError();
if ($logicError instanceof Message) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $logicError);
return;
}
$this->dbi->query($sqlQuery);
if ($request->isAjax()) {
$message = Message::success(
__('Table %1$s has been altered successfully.'),
);
$message->addParam(Current::$table);
$this->response->addJSON(
'message',
Generator::getMessage($message, $sqlQuery, 'success'),
);
$indexes = Index::getFromTable($this->dbi, Current::$table, Current::$database);
$indexesDuplicates = Index::findDuplicates(Current::$table, Current::$database);
$this->response->addJSON(
'index_table',
$this->template->render('indexes', [
'url_params' => ['db' => Current::$database, 'table' => Current::$table],
'indexes' => $indexes,
'indexes_duplicates' => $indexesDuplicates,
]),
);
return;
}
/** @var StructureController $controller */
$controller = ContainerBuilder::getContainer()->get(StructureController::class);
$controller($request);
return;
}

View File

@ -209,7 +209,7 @@ class Index
$key .= $params['Seq_in_index'];
}
if (strlen($key) <= 0) {
if ($key === '') {
return;
}

View File

@ -4,116 +4,176 @@ declare(strict_types=1);
namespace PhpMyAdmin\Table;
use PhpMyAdmin\Container\ContainerBuilder;
use PhpMyAdmin\Controllers\Table\StructureController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Index;
use PhpMyAdmin\Message;
use PhpMyAdmin\Query\Compatibility;
use PhpMyAdmin\Query\Generator as QueryGenerator;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
use function __;
use function implode;
use function in_array;
use function sprintf;
final class Indexes
{
public function __construct(
protected ResponseRenderer $response,
protected Template $template,
private DatabaseInterface $dbi,
) {
private Message|null $error = null;
public function __construct(private readonly DatabaseInterface $dbi)
{
}
public function getError(): Message|null
{
return $this->error;
}
/**
* Process the data from the edit/create index form,
* run the query to build the new index
* and moves back to /table/sql
* Function to get the sql query for index creation or edit
*
* @param Index $index An Index instance.
* @param bool $renameMode Rename the Index mode
* @param Index $index current index
*/
public function doSaveData(
ServerRequest $request,
public function getSqlQueryForIndexCreateOrEdit(
string|null $oldIndexName,
Index $index,
bool $renameMode,
string $db,
string $table,
bool $previewSql,
string $oldIndexName = '',
): void {
$error = false;
if ($renameMode && Compatibility::isCompatibleRenameIndex($this->dbi->getVersion())) {
string $dbName,
string $tableName,
): string {
// $sql_query is the one displayed in the query box
$sqlQuery = sprintf(
'ALTER TABLE %s.%s',
Util::backquote($dbName),
Util::backquote($tableName),
);
// Drops the old index
if ($oldIndexName !== null) {
if ($oldIndexName === 'PRIMARY') {
if ($index->getName() === '') {
$sqlQuery .= ' DROP PRIMARY KEY,';
} else {
$sqlQuery .= sprintf(
' DROP INDEX %s,',
Util::backquote($oldIndexName),
);
}
}
// Builds the new one
switch ($index->getChoice()) {
case 'PRIMARY':
if ($index->getName() == '') {
$index->setName('PRIMARY');
} elseif ($index->getName() !== 'PRIMARY') {
$error = Message::error(
$this->error = Message::error(
__('The name of the primary key must be "PRIMARY"!'),
);
}
$sqlQuery .= ' ADD PRIMARY KEY';
break;
case 'FULLTEXT':
case 'UNIQUE':
case 'INDEX':
case 'SPATIAL':
if ($index->getName() === 'PRIMARY') {
$this->error = Message::error(
__('Can\'t rename index to PRIMARY!'),
);
}
$sqlQuery .= sprintf(
' ADD %s',
$index->getChoice(),
);
if ($index->getName() !== '') {
$sqlQuery .= ' ' . Util::backquote($index->getName());
}
break;
}
$indexFields = [];
foreach ($index->getColumns() as $key => $column) {
$indexFields[$key] = Util::backquote($column->getName());
if (! $column->getSubPart()) {
continue;
}
$sqlQuery = QueryGenerator::getSqlQueryForIndexRename(
$db,
$table,
$oldIndexName,
$index->getName(),
);
$indexFields[$key] .= '(' . $column->getSubPart() . ')';
}
if ($indexFields === []) {
$this->error = Message::error(__('No index parts defined!'));
} else {
$sqlQuery = $this->dbi->getTable($db, $table)
->getSqlQueryForIndexCreateOrEdit($index, $error);
$sqlQuery .= ' (' . implode(', ', $indexFields) . ')';
}
// If there is a request for SQL previewing.
if ($previewSql) {
$this->response->addJSON(
'sql_data',
$this->template->render('preview_sql', ['query_data' => $sqlQuery]),
);
return;
$keyBlockSizes = $index->getKeyBlockSize();
if ($keyBlockSizes !== 0) {
$sqlQuery .= ' KEY_BLOCK_SIZE = ' . $keyBlockSizes;
}
if ($error) {
$this->response->setRequestStatus(false);
$this->response->addJSON('message', $error);
return;
// specifying index type is allowed only for primary, unique and index only
// TokuDB is using Fractal Tree, Using Type is not useless
// Ref: https://mariadb.com/kb/en/storage-engine-index-types/
$type = $index->getType();
if (
$index->getChoice() !== 'SPATIAL'
&& $index->getChoice() !== 'FULLTEXT'
&& in_array($type, Index::getIndexTypes(), true)
&& ! $this->dbi->getTable($dbName, $tableName)->isEngine('TOKUDB')
) {
$sqlQuery .= ' USING ' . $type;
}
$this->dbi->query($sqlQuery);
if ($request->isAjax()) {
$message = Message::success(
__('Table %1$s has been altered successfully.'),
);
$message->addParam($table);
$this->response->addJSON(
'message',
Generator::getMessage($message, $sqlQuery, 'success'),
);
$indexes = Index::getFromTable($this->dbi, $table, $db);
$indexesDuplicates = Index::findDuplicates($table, $db);
$this->response->addJSON(
'index_table',
$this->template->render('indexes', [
'url_params' => ['db' => $db, 'table' => $table],
'indexes' => $indexes,
'indexes_duplicates' => $indexesDuplicates,
]),
);
return;
$parser = $index->getParser();
if ($index->getChoice() === 'FULLTEXT' && $parser !== '') {
$sqlQuery .= ' WITH PARSER ' . $parser;
}
/** @var StructureController $controller */
$controller = ContainerBuilder::getContainer()->get(StructureController::class);
$controller($request);
$comment = $index->getComment();
if ($comment !== '') {
$sqlQuery .= sprintf(
' COMMENT %s',
$this->dbi->quoteString($comment),
);
}
$sqlQuery .= ';';
return $sqlQuery;
}
public function getSqlQueryForRename(string $oldIndexName, Index $index, string $db, string $table): string
{
if (! Compatibility::isCompatibleRenameIndex($this->dbi->getVersion())) {
return $this->getSqlQueryForIndexCreateOrEdit($oldIndexName, $index, $db, $table);
}
if ($oldIndexName === 'PRIMARY') {
if ($index->getName() === '') {
$index->setName('PRIMARY');
} elseif ($index->getName() !== 'PRIMARY') {
$this->error = Message::error(
__('The name of the primary key must be "PRIMARY"!'),
);
}
}
if ($index->getName() === 'PRIMARY') {
$this->error = Message::error(
__('Can\'t rename index to PRIMARY!'),
);
}
return QueryGenerator::getSqlQueryForIndexRename(
$db,
$table,
$oldIndexName,
$index->getName(),
);
}
public function executeAddIndexSql(string|DatabaseName $db, string $sql): Message

View File

@ -1875,120 +1875,6 @@ class Table implements Stringable
return Index::singleton($this->dbi, $this->dbName, $this->name, $index);
}
/**
* Function to get the sql query for index creation or edit
*
* @param Index $index current index
* @param bool $error whether error occurred or not
*/
public function getSqlQueryForIndexCreateOrEdit(Index $index, bool &$error): string
{
// $sql_query is the one displayed in the query box
$sqlQuery = sprintf(
'ALTER TABLE %s.%s',
Util::backquote($this->dbName),
Util::backquote($this->name),
);
// Drops the old index
if (! empty($_POST['old_index'])) {
$oldIndex = is_array($_POST['old_index']) ? $_POST['old_index']['Key_name'] : $_POST['old_index'];
if ($oldIndex === 'PRIMARY') {
$sqlQuery .= ' DROP PRIMARY KEY,';
} else {
$sqlQuery .= sprintf(
' DROP INDEX %s,',
Util::backquote($oldIndex),
);
}
}
// Builds the new one
switch ($index->getChoice()) {
case 'PRIMARY':
if ($index->getName() == '') {
$index->setName('PRIMARY');
} elseif ($index->getName() !== 'PRIMARY') {
$error = Message::error(
__('The name of the primary key must be "PRIMARY"!'),
);
}
$sqlQuery .= ' ADD PRIMARY KEY';
break;
case 'FULLTEXT':
case 'UNIQUE':
case 'INDEX':
case 'SPATIAL':
if ($index->getName() === 'PRIMARY') {
$error = Message::error(
__('Can\'t rename index to PRIMARY!'),
);
}
$sqlQuery .= sprintf(
' ADD %s',
$index->getChoice(),
);
if ($index->getName() !== '') {
$sqlQuery .= ' ' . Util::backquote($index->getName());
}
break;
}
$indexFields = [];
foreach ($index->getColumns() as $key => $column) {
$indexFields[$key] = Util::backquote($column->getName());
if (! $column->getSubPart()) {
continue;
}
$indexFields[$key] .= '(' . $column->getSubPart() . ')';
}
if ($indexFields === []) {
$error = Message::error(__('No index parts defined!'));
} else {
$sqlQuery .= ' (' . implode(', ', $indexFields) . ')';
}
$keyBlockSizes = $index->getKeyBlockSize();
if ($keyBlockSizes !== 0) {
$sqlQuery .= ' KEY_BLOCK_SIZE = ' . $keyBlockSizes;
}
// specifying index type is allowed only for primary, unique and index only
// TokuDB is using Fractal Tree, Using Type is not useless
// Ref: https://mariadb.com/kb/en/storage-engine-index-types/
$type = $index->getType();
if (
$index->getChoice() !== 'SPATIAL'
&& $index->getChoice() !== 'FULLTEXT'
&& in_array($type, Index::getIndexTypes(), true)
&& ! $this->isEngine('TOKUDB')
) {
$sqlQuery .= ' USING ' . $type;
}
$parser = $index->getParser();
if ($index->getChoice() === 'FULLTEXT' && $parser !== '') {
$sqlQuery .= ' WITH PARSER ' . $parser;
}
$comment = $index->getComment();
if ($comment !== '') {
$sqlQuery .= sprintf(
' COMMENT %s',
$this->dbi->quoteString($comment),
);
}
$sqlQuery .= ';';
return $sqlQuery;
}
/**
* Function to handle update for display field
*

View File

@ -24,31 +24,32 @@ class IndexRenameControllerTest extends AbstractTestCase
public function testIndexRenameController(): void
{
Current::$database = 'test_db';
Current::$table = 'test_table';
Current::$table = 'test_table_index_rename';
$GLOBALS['lang'] = 'en';
$dummyDbi = $this->createDbiDummy();
$dummyDbi->addSelectDb('test_db');
$dummyDbi->addSelectDb('test_db');
$dummyDbi->addResult('SELECT 1 FROM `test_db`.`test_table` LIMIT 1;', [['1']]);
$dummyDbi->addResult('SELECT 1 FROM `test_db`.`test_table_index_rename` LIMIT 1;', [['1']]);
$dbi = $this->createDatabaseInterface($dummyDbi);
DatabaseInterface::$instance = $dbi;
$template = new Template();
$expected = $template->render('table/index_rename_form', [
'index' => new Index(),
'form_params' => ['db' => 'test_db', 'table' => 'test_table'],
'index' => new Index(['Key_name' => 'index']),
'form_params' => ['db' => 'test_db', 'table' => 'test_table_index_rename', 'old_index' => 'index'],
]);
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/')
->withQueryParams(['db' => 'test_db', 'table' => 'test_table']);
->withQueryParams(['db' => 'test_db', 'table' => 'test_table_index_rename'])
->withParsedBody(['index' => 'index']);
$response = new ResponseRenderer();
(new IndexRenameController(
$response,
$template,
$dbi,
new Indexes($response, $template, $dbi),
new Indexes($dbi),
new DbTableExists($dbi),
))($request);
$this->assertSame($expected, $response->getHTMLResult());
@ -62,9 +63,9 @@ class IndexRenameControllerTest extends AbstractTestCase
Config::getInstance()->selectedServer['DisableIS'] = true;
Current::$database = 'test_db';
Current::$table = 'test_table';
Current::$table = 'test_table_index_rename';
$_POST['db'] = 'test_db';
$_POST['table'] = 'test_table';
$_POST['table'] = 'test_table_index_rename';
$_POST['old_index'] = 'old_name';
$_POST['index'] = ['Key_name' => 'new_name'];
$_POST['do_save_data'] = '1';
@ -72,12 +73,12 @@ class IndexRenameControllerTest extends AbstractTestCase
$dbiDummy = $this->createDbiDummy();
$dbiDummy->addSelectDb('test_db');
$dbiDummy->addResult('SELECT 1 FROM `test_db`.`test_table` LIMIT 1;', [['1']], ['1']);
$dbiDummy->addResult('SELECT 1 FROM `test_db`.`test_table_index_rename` LIMIT 1;', [['1']], ['1']);
$dbiDummy->addResult(
'SHOW INDEXES FROM `test_db`.`test_table`',
'SHOW INDEXES FROM `test_db`.`test_table_index_rename`',
[
['test_table', '0', 'PRIMARY', 'id', 'BTREE'],
['test_table', '1', 'old_name', 'name', 'BTREE'],
['test_table_index_rename', '0', 'PRIMARY', 'id', 'BTREE'],
['test_table_index_rename', '1', 'old_name', 'name', 'BTREE'],
],
['Table', 'Non_unique', 'Key_name', 'Column_name', 'Index_type'],
);
@ -89,17 +90,17 @@ class IndexRenameControllerTest extends AbstractTestCase
$expected = <<<'HTML'
<div class="preview_sql">
<code class="sql" dir="ltr"><pre>
ALTER TABLE `test_db`.`test_table` DROP INDEX `old_name`, ADD INDEX `new_name` (`name`) USING BTREE;
ALTER TABLE `test_db`.`test_table_index_rename` DROP INDEX `old_name`, ADD INDEX `new_name` (`name`) USING BTREE;
</pre></code>
</div>
HTML;
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/')
->withQueryParams(['db' => 'test_db', 'table' => 'test_table'])
->withQueryParams(['db' => 'test_db', 'table' => 'test_table_index_rename'])
->withParsedBody([
'old_index' => 'old_name',
'index' => ['Key_name' => 'new_name'],
'index' => 'new_name',
'do_save_data' => '1',
'preview_sql' => '1',
]);
@ -110,7 +111,7 @@ HTML;
$responseRenderer,
$template,
$dbi,
new Indexes($responseRenderer, $template, $dbi),
new Indexes($dbi),
new DbTableExists($dbi),
);
$controller($request);

View File

@ -96,7 +96,7 @@ class IndexesControllerTest extends AbstractTestCase
$response,
$template,
$dbi,
new Indexes($response, $template, $dbi),
new Indexes($dbi),
new DbTableExists($dbi),
);

View File

@ -36,7 +36,7 @@ class SpatialControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new SpatialController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);
@ -64,7 +64,7 @@ class SpatialControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new SpatialController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);
@ -90,7 +90,7 @@ class SpatialControllerTest extends AbstractTestCase
$controllerStub->expects($this->never())->method('__invoke');
$response = new ResponseRenderer();
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new SpatialController($response, new Template(), $controllerStub, $indexes);
$controller($request);
@ -120,7 +120,7 @@ class SpatialControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new SpatialController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);

View File

@ -36,7 +36,7 @@ class UniqueControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new UniqueController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);
@ -64,7 +64,7 @@ class UniqueControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new UniqueController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);
@ -90,7 +90,7 @@ class UniqueControllerTest extends AbstractTestCase
$controllerStub->expects($this->never())->method('__invoke');
$response = new ResponseRenderer();
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new UniqueController($response, new Template(), $controllerStub, $indexes);
$controller($request);
@ -120,7 +120,7 @@ class UniqueControllerTest extends AbstractTestCase
$controllerStub = $this->createMock(StructureController::class);
$controllerStub->expects($this->once())->method('__invoke')->with($request);
$indexes = new Indexes(new ResponseRenderer(), new Template(), DatabaseInterface::getInstance());
$indexes = new Indexes(DatabaseInterface::getInstance());
$controller = new UniqueController(new ResponseRenderer(), new Template(), $controllerStub, $indexes);
$controller($request);

View File

@ -1651,6 +1651,11 @@ class DbiDummy implements DbiExtension
'columns' => ['Name', 'Engine', 'Rows'],
'result' => [['test_table', 'InnoDB', '3']],
],
[
'query' => 'SHOW TABLE STATUS FROM `test_db` WHERE `Name` LIKE \'test\\\\_table\\\\_index\\\\_rename%\'',
'columns' => ['Name', 'Engine', 'Rows'],
'result' => [['test_table_index_rename', 'InnoDB', '3']],
],
[
'query' => 'SHOW TABLE STATUS FROM `test_db` WHERE Name = \'test_table\'',
'columns' => ['Name', 'Engine', 'Rows'],
@ -1661,6 +1666,11 @@ class DbiDummy implements DbiExtension
'columns' => ['Table', 'Non_unique', 'Key_name', 'Column_name'],
'result' => [['test_table', '0', 'PRIMARY', 'id']],
],
[
'query' => 'SHOW INDEXES FROM `test_db`.`test_table_index_rename`',
'columns' => ['Table', 'Non_unique', 'Key_name', 'Column_name'],
'result' => [['test_table_index_rename', '0', 'PRIMARY', 'id']],
],
[
'query' => 'SHOW INDEX FROM `test_table`;',
'columns' => ['Table', 'Non_unique', 'Key_name', 'Column_name'],

View File

@ -4,86 +4,86 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Table;
use PhpMyAdmin\Config;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Index;
use PhpMyAdmin\Message;
use PhpMyAdmin\Table\Indexes;
use PhpMyAdmin\Table\Table;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
#[CoversClass(Indexes::class)]
class IndexesTest extends AbstractTestCase
{
private DatabaseInterface&MockObject $dbi;
protected function setUp(): void
{
parent::setUp();
/**
* SET these to avoid undefined index error
*/
Current::$database = 'db';
Current::$table = 'table';
$config = Config::getInstance();
$config->selectedServer['pmadb'] = '';
$config->selectedServer['DisableIS'] = false;
$GLOBALS['urlParams'] = ['db' => 'db', 'server' => 1];
$dbi = $this->getMockBuilder(DatabaseInterface::class)
$this->dbi = $this->getMockBuilder(DatabaseInterface::class)
->disableOriginalConstructor()
->getMock();
$indexs = [
['Schema' => 'Schema1', 'Key_name' => 'Key_name1', 'Column_name' => 'Column_name1'],
['Schema' => 'Schema2', 'Key_name' => 'Key_name2', 'Column_name' => 'Column_name2'],
['Schema' => 'Schema3', 'Key_name' => 'Key_name3', 'Column_name' => 'Column_name3'],
];
$dbi->expects($this->any())->method('getTableIndexes')
->willReturn($indexs);
DatabaseInterface::$instance = $dbi;
//$_SESSION
}
public function testDoSaveData(): void
public function testGetSqlQueryForRename(): void
{
$sqlQuery = 'ALTER TABLE `db`.`table` DROP PRIMARY KEY, ADD UNIQUE ;';
$sqlQuery = 'ALTER TABLE `db`.`table` RENAME INDEX `0` TO `ABC`;';
$this->dbi->expects($this->any())->method('getVersion')
->willReturn(50700);
$index = new Index(['Key_name' => 'ABC']);
$indexes = new Indexes($this->dbi);
$sqlResult = $indexes->getSqlQueryForRename('0', $index, Current::$database, Current::$table);
$this->assertStringContainsString($sqlQuery, $sqlResult);
// Error message
$index->setName('NOT PRIMARY'); // Cannot rename primary so the operation should fail
$indexes->getSqlQueryForRename('PRIMARY', $index, Current::$database, Current::$table);
$error = $indexes->getError();
$this->assertInstanceOf(Message::class, $error);
$index->setName('PRIMARY'); // The new name cannot be PRIMARY so the operation should fail
$indexes->getSqlQueryForRename('NOT PRIMARY', $index, Current::$database, Current::$table);
$error = $indexes->getError();
$this->assertInstanceOf(Message::class, $error);
}
public function testGetSqlQueryForIndexCreateOrEdit(): void
{
$table = $this->getMockBuilder(Table::class)
->disableOriginalConstructor()
->getMock();
$table->expects($this->any())->method('getSqlQueryForIndexCreateOrEdit')
->willReturn($sqlQuery);
$dbi = DatabaseInterface::getInstance();
$dbi->expects($this->any())->method('getTable')
$this->dbi->expects($this->any())->method('getTable')
->willReturn($table);
$indexes = new Indexes($this->dbi);
$response = new ResponseStub();
$index = new Index();
$db = 'pma_db';
$table = 'pma_table';
$index = new Index([
'Key_name' => 'PRIMARY',
'columns' => [['Column_name' => 'id']],
]);
$indexes = new Indexes($response, new Template(), $dbi);
$sqlQueryExpected = 'ALTER TABLE `pma_db`.`pma_table` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`);';
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/')
->withQueryParams(['ajax_request' => '1']);
$_POST['old_index'] = 'PRIMARY';
$this->assertEquals(
$sqlQueryExpected,
$indexes->getSqlQueryForIndexCreateOrEdit('PRIMARY', $index, $db, $table),
);
// Preview SQL
$indexes->doSaveData($request, $index, false, Current::$database, Current::$table, true);
$jsonArray = $response->getJSONResult();
$this->assertArrayHasKey('sql_data', $jsonArray);
$this->assertStringContainsString($sqlQuery, $jsonArray['sql_data']);
// Alter success
$response->clear();
$indexes->doSaveData($request, $index, false, Current::$database, Current::$table, false);
$jsonArray = $response->getJSONResult();
$this->assertArrayHasKey('index_table', $jsonArray);
$this->assertArrayHasKey('message', $jsonArray);
// Error message
$index->setName('NOT PRIMARY'); // Cannot rename primary so the operation should fail
$indexes->getSqlQueryForIndexCreateOrEdit('PRIMARY', $index, $db, $table);
$this->assertInstanceOf(Message::class, $indexes->getError());
}
}

View File

@ -9,7 +9,6 @@ use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\ConfigStorage\RelationParameters;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Dbal\Connection;
use PhpMyAdmin\Index;
use PhpMyAdmin\ListDatabase;
use PhpMyAdmin\Query\Cache;
use PhpMyAdmin\SqlParser\Context;
@ -1180,42 +1179,6 @@ class TableTest extends AbstractTestCase
$this->assertEquals($sqlExcepted, $sql);
}
/**
* Tests for getSqlQueryForIndexCreateOrEdit() method.
*/
public function testGetSqlQueryForIndexCreateOrEdit(): void
{
$db = 'pma_db';
$table = 'pma_table';
$index = new Index();
$error = false;
$_POST['old_index'] = 'PRIMARY';
$table = new Table($table, $db, DatabaseInterface::getInstance());
$sql = $table->getSqlQueryForIndexCreateOrEdit($index, $error);
$this->assertEquals('ALTER TABLE `pma_db`.`pma_table` DROP PRIMARY KEY, ADD UNIQUE;', $sql);
}
/**
* Tests for getSqlQueryForIndexCreateOrEdit() method.
*/
public function testGetSqlQueryForIndexCreateOrEditSecondFormat(): void
{
$db = 'pma_db';
$table = 'pma_table';
$index = new Index();
$error = false;
$_POST['old_index']['Key_name'] = 'PRIMARY';
$table = new Table($table, $db, DatabaseInterface::getInstance());
$sql = $table->getSqlQueryForIndexCreateOrEdit($index, $error);
$this->assertEquals('ALTER TABLE `pma_db`.`pma_table` DROP PRIMARY KEY, ADD UNIQUE;', $sql);
}
/**
* Test for getColumns
*/