Create TableMover class
Signed-off-by: Kamil Tekiela <tekiela246@gmail.com>
This commit is contained in:
parent
accafad56b
commit
81853e877b
@ -1906,7 +1906,7 @@ parameters:
|
||||
path: src/Controllers/Database/Structure/CentralColumns/RemoveController.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#5 \\$what of static method PhpMyAdmin\\\\Table\\\\Table\\:\\:moveCopy\\(\\) expects string, mixed given\\.$#"
|
||||
message: "#^Parameter \\#5 \\$what of static method PhpMyAdmin\\\\Table\\\\TableMover\\:\\:moveCopy\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: src/Controllers/Database/Structure/CopyTableController.php
|
||||
|
||||
@ -9681,7 +9681,7 @@ parameters:
|
||||
path: src/Operations.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#5 \\$what of static method PhpMyAdmin\\\\Table\\\\Table\\:\\:moveCopy\\(\\) expects string, mixed given\\.$#"
|
||||
message: "#^Parameter \\#5 \\$what of static method PhpMyAdmin\\\\Table\\\\TableMover\\:\\:moveCopy\\(\\) expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
path: src/Operations.php
|
||||
|
||||
@ -13930,11 +13930,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Table/Search.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\>\\|null supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
@ -13942,7 +13937,7 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
|
||||
count: 3
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
@ -14040,19 +14035,9 @@ parameters:
|
||||
count: 6
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method has\\(\\) on PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\.$#"
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
||||
count: 17
|
||||
count: 13
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
@ -14110,11 +14095,6 @@ parameters:
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, bool\\|string given\\.$#"
|
||||
count: 1
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, int\\|false given\\.$#"
|
||||
count: 1
|
||||
@ -14125,11 +14105,6 @@ parameters:
|
||||
count: 1
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
|
||||
count: 2
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in an elseif condition, int\\|false given\\.$#"
|
||||
count: 1
|
||||
@ -14160,21 +14135,11 @@ parameters:
|
||||
count: 6
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$list of class PhpMyAdmin\\\\SqlParser\\\\Parser constructor expects PhpMyAdmin\\\\SqlParser\\\\TokensList\\|PhpMyAdmin\\\\SqlParser\\\\UtfString\\|string\\|null, mixed given\\.$#"
|
||||
count: 3
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of method PhpMyAdmin\\\\DatabaseInterface\\:\\:quoteString\\(\\) expects string, mixed given\\.$#"
|
||||
count: 9
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of method PhpMyAdmin\\\\DatabaseInterface\\:\\:quoteString\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 6
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of function substr expects string, mixed given\\.$#"
|
||||
count: 2
|
||||
@ -14240,6 +14205,51 @@ parameters:
|
||||
count: 1
|
||||
path: src/Table/Table.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\AlterOperation\\>\\|null supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 2
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
|
||||
count: 1
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method has\\(\\) on PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\.$#"
|
||||
count: 2
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 2
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
|
||||
count: 4
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, bool\\|string given\\.$#"
|
||||
count: 1
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
|
||||
count: 2
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$list of class PhpMyAdmin\\\\SqlParser\\\\Parser constructor expects PhpMyAdmin\\\\SqlParser\\\\TokensList\\|PhpMyAdmin\\\\SqlParser\\\\UtfString\\|string\\|null, mixed given\\.$#"
|
||||
count: 3
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$str of method PhpMyAdmin\\\\DatabaseInterface\\:\\:quoteString\\(\\) expects string, string\\|null given\\.$#"
|
||||
count: 6
|
||||
path: src/Table/TableMover.php
|
||||
|
||||
-
|
||||
message: "#^Call to function in_array\\(\\) requires parameter \\#3 to be set\\.$#"
|
||||
count: 1
|
||||
|
||||
@ -11228,15 +11228,7 @@
|
||||
<code><![CDATA[Config::getInstance()]]></code>
|
||||
<code><![CDATA[Config::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
</DeprecatedMethod>
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$GLOBALS['add_constraints']]]></code>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_auto_increments']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_indexes']]]></code>
|
||||
</InvalidArrayOffset>
|
||||
<InvalidReturnStatement>
|
||||
<code><![CDATA[$tableAutoIncrement ?? '']]></code>
|
||||
<code><![CDATA[$this->getStatusInfo('TABLE_COLLATION') ?? '']]></code>
|
||||
@ -11248,8 +11240,6 @@
|
||||
<code><![CDATA[string]]></code>
|
||||
</InvalidReturnType>
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$GLOBALS['sql_auto_increments']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_indexes']]]></code>
|
||||
<code><![CDATA[$_POST['constraint_name'][$masterFieldMd5]]]></code>
|
||||
<code><![CDATA[$column]]></code>
|
||||
<code><![CDATA[$column['Extra']]]></code>
|
||||
@ -11345,9 +11335,6 @@
|
||||
<code><![CDATA[$optionsArray[$existrelForeign[$masterFieldMd5]['on_update'] ?? '']]]></code>
|
||||
</MixedArrayTypeCoercion>
|
||||
<MixedAssignment>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$column]]></code>
|
||||
<code><![CDATA[$column]]></code>
|
||||
<code><![CDATA[$columns[$row['Field']]]]></code>
|
||||
@ -11371,30 +11358,16 @@
|
||||
<code><![CDATA[$ret[]]]></code>
|
||||
<code><![CDATA[$row]]></code>
|
||||
<code><![CDATA[$value]]></code>
|
||||
<code><![CDATA[$value]]></code>
|
||||
<code><![CDATA[$value]]></code>
|
||||
</MixedAssignment>
|
||||
<MixedInferredReturnType>
|
||||
<code><![CDATA[mixed[]]]></code>
|
||||
<code><![CDATA[string]]></code>
|
||||
<code><![CDATA[string]]></code>
|
||||
</MixedInferredReturnType>
|
||||
<MixedMethodCall>
|
||||
<code><![CDATA[has]]></code>
|
||||
<code><![CDATA[has]]></code>
|
||||
</MixedMethodCall>
|
||||
<MixedOperand>
|
||||
<code><![CDATA[$backquoted ? Util::backquote($column) : $column]]></code>
|
||||
<code><![CDATA[$index[0]]]></code>
|
||||
</MixedOperand>
|
||||
<MixedPropertyAssignment>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
</MixedPropertyAssignment>
|
||||
<MixedPropertyFetch>
|
||||
<code><![CDATA[$altered->options]]></code>
|
||||
<code><![CDATA[$altered->options]]></code>
|
||||
</MixedPropertyFetch>
|
||||
<MixedReturnStatement>
|
||||
<code><![CDATA[end($this->errors)]]></code>
|
||||
<code><![CDATA[end($this->messages)]]></code>
|
||||
@ -11418,15 +11391,9 @@
|
||||
<code><![CDATA[$_POST['on_update'][$masterFieldMd5]]]></code>
|
||||
</PossiblyInvalidArrayOffset>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$commentsCopyRow['column_name']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['comment']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['mimetype']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['transformation']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['transformation_options']]]></code>
|
||||
<code><![CDATA[$existrelForeign[$masterFieldMd5]['ref_db_name']]]></code>
|
||||
<code><![CDATA[$existrelForeign[$masterFieldMd5]['ref_index_list']]]></code>
|
||||
<code><![CDATA[$existrelForeign[$masterFieldMd5]['ref_table_name']]]></code>
|
||||
<code><![CDATA[$val]]></code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullArrayAccess>
|
||||
<code><![CDATA[$existrelForeign[$masterFieldMd5]['constraint']]]></code>
|
||||
@ -11441,10 +11408,6 @@
|
||||
<code><![CDATA[$optionsArray]]></code>
|
||||
<code><![CDATA[$optionsArray]]></code>
|
||||
</PossiblyNullArrayOffset>
|
||||
<PossiblyNullIterator>
|
||||
<code><![CDATA[$statement->altered]]></code>
|
||||
<code><![CDATA[$statement->altered]]></code>
|
||||
</PossiblyNullIterator>
|
||||
<PossiblyNullOperand>
|
||||
<code><![CDATA[Generator::mysqlDie($tmpErrorCreate, $createQuery, false, '', false)]]></code>
|
||||
<code><![CDATA[Generator::mysqlDie($tmpErrorDrop, $dropQuery, false, '', false)]]></code>
|
||||
@ -11456,19 +11419,68 @@
|
||||
<PossiblyUnusedReturnValue>
|
||||
<code><![CDATA[true|Message]]></code>
|
||||
</PossiblyUnusedReturnValue>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<code><![CDATA[! Context::isKeyword($columnName, true)]]></code>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
<UnsupportedPropertyReferenceUsage>
|
||||
<code><![CDATA[$this->uiprefs =& $_SESSION['tmpval']['table_uiprefs'][$serverId][$this->dbName][$this->name]]]></code>
|
||||
</UnsupportedPropertyReferenceUsage>
|
||||
</file>
|
||||
<file src="src/Table/TableMover.php">
|
||||
<DeprecatedMethod>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
|
||||
</DeprecatedMethod>
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$GLOBALS['add_constraints']]]></code>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_auto_increments']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_indexes']]]></code>
|
||||
</InvalidArrayOffset>
|
||||
<MixedArgument>
|
||||
<code><![CDATA[$GLOBALS['sql_auto_increments']]]></code>
|
||||
<code><![CDATA[$GLOBALS['sql_indexes']]]></code>
|
||||
</MixedArgument>
|
||||
<MixedAssignment>
|
||||
<code><![CDATA[$GLOBALS['errorUrl']]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$value]]></code>
|
||||
<code><![CDATA[$value]]></code>
|
||||
</MixedAssignment>
|
||||
<MixedMethodCall>
|
||||
<code><![CDATA[has]]></code>
|
||||
<code><![CDATA[has]]></code>
|
||||
</MixedMethodCall>
|
||||
<MixedPropertyAssignment>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
<code><![CDATA[$altered]]></code>
|
||||
</MixedPropertyAssignment>
|
||||
<MixedPropertyFetch>
|
||||
<code><![CDATA[$altered->options]]></code>
|
||||
<code><![CDATA[$altered->options]]></code>
|
||||
</MixedPropertyFetch>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$commentsCopyRow['column_name']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['comment']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['mimetype']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['transformation']]]></code>
|
||||
<code><![CDATA[$commentsCopyRow['transformation_options']]]></code>
|
||||
<code><![CDATA[$val]]></code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullIterator>
|
||||
<code><![CDATA[$statement->altered]]></code>
|
||||
<code><![CDATA[$statement->altered]]></code>
|
||||
</PossiblyNullIterator>
|
||||
<RedundantConditionGivenDocblockType>
|
||||
<code><![CDATA[! empty($parser->statements[0])]]></code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<RiskyTruthyFalsyComparison>
|
||||
<code><![CDATA[! $relationParams[$work]]]></code>
|
||||
<code><![CDATA[! Context::isKeyword($columnName, true)]]></code>
|
||||
</RiskyTruthyFalsyComparison>
|
||||
<TypeDoesNotContainType>
|
||||
<code><![CDATA[! empty($GLOBALS['sql_constraints_query'])]]></code>
|
||||
</TypeDoesNotContainType>
|
||||
<UnsupportedPropertyReferenceUsage>
|
||||
<code><![CDATA[$this->uiprefs =& $_SESSION['tmpval']['table_uiprefs'][$serverId][$this->dbName][$this->name]]]></code>
|
||||
</UnsupportedPropertyReferenceUsage>
|
||||
<UnusedVariable>
|
||||
<code><![CDATA[$maintainRelations]]></code>
|
||||
</UnusedVariable>
|
||||
|
||||
@ -11,7 +11,7 @@ use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Operations;
|
||||
use PhpMyAdmin\Table\Table;
|
||||
use PhpMyAdmin\Table\TableMover;
|
||||
use PhpMyAdmin\UserPrivilegesFactory;
|
||||
|
||||
final class CopyTableController implements InvocableController
|
||||
@ -33,7 +33,7 @@ final class CopyTableController implements InvocableController
|
||||
$userPrivileges = $this->userPrivilegesFactory->getPrivileges();
|
||||
|
||||
foreach ($selected as $selectedValue) {
|
||||
Table::moveCopy(
|
||||
TableMover::moveCopy(
|
||||
Current::$database,
|
||||
$selectedValue,
|
||||
$targetDb,
|
||||
|
||||
@ -10,7 +10,7 @@ use PhpMyAdmin\Current;
|
||||
use PhpMyAdmin\Http\Response;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Table\Table;
|
||||
use PhpMyAdmin\Table\TableMover;
|
||||
|
||||
use function mb_strlen;
|
||||
use function mb_substr;
|
||||
@ -33,7 +33,7 @@ final class CopyTableWithPrefixController implements InvocableController
|
||||
foreach ($selected as $selectedValue) {
|
||||
$newTableName = $toPrefix . mb_substr($selectedValue, mb_strlen((string) $fromPrefix));
|
||||
|
||||
Table::moveCopy(
|
||||
TableMover::moveCopy(
|
||||
Current::$database,
|
||||
$selectedValue,
|
||||
Current::$database,
|
||||
|
||||
@ -12,6 +12,7 @@ use PhpMyAdmin\Identifiers\DatabaseName;
|
||||
use PhpMyAdmin\Partitioning\Partition;
|
||||
use PhpMyAdmin\Plugins\Export\ExportSql;
|
||||
use PhpMyAdmin\Table\Table;
|
||||
use PhpMyAdmin\Table\TableMover;
|
||||
use PhpMyAdmin\Triggers\Triggers;
|
||||
|
||||
use function __;
|
||||
@ -193,7 +194,7 @@ class Operations
|
||||
$triggers = Triggers::getDetails($this->dbi, $db, $table);
|
||||
|
||||
if (
|
||||
! Table::moveCopy(
|
||||
! TableMover::moveCopy(
|
||||
$db,
|
||||
$table,
|
||||
$newDatabaseName->getName(),
|
||||
@ -268,7 +269,7 @@ class Operations
|
||||
{
|
||||
// Add DROP IF EXIST to CREATE VIEW query, to remove stand-in VIEW that was created earlier.
|
||||
foreach ($views as $view) {
|
||||
$copyingSucceeded = Table::moveCopy(
|
||||
$copyingSucceeded = TableMover::moveCopy(
|
||||
$db,
|
||||
$view,
|
||||
$newDatabaseName->getName(),
|
||||
@ -459,7 +460,7 @@ class Operations
|
||||
$getFields = ['user', 'label', 'query'];
|
||||
$whereFields = ['dbase' => $db];
|
||||
$newFields = ['dbase' => $newDatabaseName->getName()];
|
||||
Table::duplicateInfo('bookmarkwork', 'bookmark', $getFields, $whereFields, $newFields);
|
||||
TableMover::duplicateInfo('bookmarkwork', 'bookmark', $getFields, $whereFields, $newFields);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -887,7 +888,7 @@ class Operations
|
||||
$message = Message::error(__('Can\'t copy table to same one!'));
|
||||
}
|
||||
} else {
|
||||
Table::moveCopy(
|
||||
TableMover::moveCopy(
|
||||
$db,
|
||||
$table,
|
||||
$targetDb,
|
||||
|
||||
@ -17,17 +17,11 @@ use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Html\MySQLDocumentation;
|
||||
use PhpMyAdmin\Index;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\Plugins\Export\ExportSql;
|
||||
use PhpMyAdmin\Query\Compatibility;
|
||||
use PhpMyAdmin\Query\Generator as QueryGenerator;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\AlterStatement;
|
||||
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
|
||||
use PhpMyAdmin\SqlParser\Statements\DropStatement;
|
||||
use PhpMyAdmin\SqlParser\Utils\Table as TableUtils;
|
||||
use PhpMyAdmin\Triggers\Triggers;
|
||||
use PhpMyAdmin\Util;
|
||||
@ -766,504 +760,6 @@ class Table implements Stringable
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts existing entries in a PMA_* table by reading a value from an old
|
||||
* entry
|
||||
*
|
||||
* @param string $work The array index, which Relation feature to check ('relwork', 'commwork', ...)
|
||||
* @param string $table The array index, which PMA-table to update ('bookmark', 'relation', ...)
|
||||
* @param string[] $getFields Which fields will be SELECT'ed from the old entry
|
||||
* @param mixed[] $whereFields Which fields will be used for the WHERE query (array('FIELDNAME' => 'FIELDVALUE'))
|
||||
* @param mixed[] $newFields Which fields will be used as new VALUES. These are the important keys which differ
|
||||
* from the old entry (array('FIELDNAME' => 'NEW FIELDVALUE'))
|
||||
*/
|
||||
public static function duplicateInfo(
|
||||
string $work,
|
||||
string $table,
|
||||
array $getFields,
|
||||
array $whereFields,
|
||||
array $newFields,
|
||||
): int|bool {
|
||||
$dbi = DatabaseInterface::getInstance();
|
||||
$relation = new Relation($dbi);
|
||||
$relationParameters = $relation->getRelationParameters();
|
||||
$relationParams = $relationParameters->toArray();
|
||||
$lastId = -1;
|
||||
|
||||
if (! isset($relationParams[$work], $relationParams[$table]) || ! $relationParams[$work]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$selectParts = [];
|
||||
$rowFields = [];
|
||||
foreach ($getFields as $getField) {
|
||||
$selectParts[] = Util::backquote($getField);
|
||||
$rowFields[] = $getField;
|
||||
}
|
||||
|
||||
$whereParts = [];
|
||||
foreach ($whereFields as $where => $value) {
|
||||
$whereParts[] = Util::backquote((string) $where) . ' = '
|
||||
. $dbi->quoteString((string) $value, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$newParts = [];
|
||||
$newValueParts = [];
|
||||
foreach ($newFields as $where => $value) {
|
||||
$newParts[] = Util::backquote((string) $where);
|
||||
$newValueParts[] = $dbi->quoteString((string) $value, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$tableCopyQuery = '
|
||||
SELECT ' . implode(', ', $selectParts) . '
|
||||
FROM ' . Util::backquote($relationParameters->db) . '.'
|
||||
. Util::backquote((string) $relationParams[$table]) . '
|
||||
WHERE ' . implode(' AND ', $whereParts);
|
||||
|
||||
// must use DatabaseInterface::QUERY_BUFFERED here, since we execute
|
||||
// another query inside the loop
|
||||
$tableCopyRs = $dbi->queryAsControlUser($tableCopyQuery);
|
||||
|
||||
foreach ($tableCopyRs as $tableCopyRow) {
|
||||
$valueParts = [];
|
||||
foreach ($tableCopyRow as $key => $val) {
|
||||
if (! in_array($key, $rowFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueParts[] = $dbi->quoteString($val, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$newTableQuery = 'INSERT IGNORE INTO '
|
||||
. Util::backquote($relationParameters->db)
|
||||
. '.' . Util::backquote((string) $relationParams[$table])
|
||||
. ' (' . implode(', ', $selectParts) . ', '
|
||||
. implode(', ', $newParts) . ') VALUES ('
|
||||
. implode(', ', $valueParts) . ', '
|
||||
. implode(', ', $newValueParts) . ')';
|
||||
|
||||
$dbi->queryAsControlUser($newTableQuery);
|
||||
$lastId = $dbi->insertId();
|
||||
}
|
||||
|
||||
return $lastId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies or renames table
|
||||
*
|
||||
* @param string $sourceDb source database
|
||||
* @param string $sourceTable source table
|
||||
* @param string $targetDb target database
|
||||
* @param string $targetTable target table
|
||||
* @param string $what what to be moved or copied (data, dataonly)
|
||||
* @param bool $move whether to move
|
||||
* @param string $mode mode
|
||||
*/
|
||||
public static function moveCopy(
|
||||
string $sourceDb,
|
||||
string $sourceTable,
|
||||
string $targetDb,
|
||||
string $targetTable,
|
||||
string $what,
|
||||
bool $move,
|
||||
string $mode,
|
||||
bool $addDropIfExists,
|
||||
): bool {
|
||||
$GLOBALS['errorUrl'] ??= null;
|
||||
$dbi = DatabaseInterface::getInstance();
|
||||
$relation = new Relation($dbi);
|
||||
|
||||
// Try moving the tables directly, using native `RENAME` statement.
|
||||
if ($move && $what === 'data') {
|
||||
$tbl = new Table($sourceTable, $sourceDb, $dbi);
|
||||
if ($tbl->rename($targetTable, $targetDb)) {
|
||||
$GLOBALS['message'] = $tbl->getLastMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting required export settings.
|
||||
$GLOBALS['asfile'] = 1;
|
||||
|
||||
// Ensuring the target database is valid.
|
||||
$databaseList = $dbi->getDatabaseList();
|
||||
if (! $databaseList->exists($sourceDb, $targetDb)) {
|
||||
if (! $databaseList->exists($sourceDb)) {
|
||||
$GLOBALS['message'] = Message::rawError(
|
||||
sprintf(
|
||||
__('Source database `%s` was not found!'),
|
||||
htmlspecialchars($sourceDb),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (! $databaseList->exists($targetDb)) {
|
||||
$GLOBALS['message'] = Message::rawError(
|
||||
sprintf(
|
||||
__('Target database `%s` was not found!'),
|
||||
htmlspecialchars($targetDb),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full name of source table, quoted.
|
||||
*/
|
||||
$source = Util::backquote($sourceDb) . '.' . Util::backquote($sourceTable);
|
||||
|
||||
// If the target database is not specified, the operation is taking
|
||||
// place in the same database.
|
||||
if ($targetDb === '') {
|
||||
$targetDb = $sourceDb;
|
||||
}
|
||||
|
||||
// Selecting the database could avoid some problems with replicated
|
||||
// databases, when moving table from replicated one to not replicated one.
|
||||
$dbi->selectDb($targetDb);
|
||||
|
||||
/**
|
||||
* The full name of target table, quoted.
|
||||
*/
|
||||
$target = Util::backquote($targetDb) . '.' . Util::backquote($targetTable);
|
||||
|
||||
// No table is created when this is a data-only operation.
|
||||
if ($what !== 'dataonly') {
|
||||
/**
|
||||
* Instance used for exporting the current structure of the table.
|
||||
*
|
||||
* @var ExportSql $exportSqlPlugin
|
||||
*/
|
||||
$exportSqlPlugin = Plugins::getPlugin('export', 'sql', [
|
||||
'export_type' => 'table',
|
||||
'single_table' => false,
|
||||
]);
|
||||
// It is better that all identifiers are quoted
|
||||
$exportSqlPlugin->useSqlBackquotes(true);
|
||||
|
||||
$noConstraintsComments = true;
|
||||
$GLOBALS['sql_constraints_query'] = '';
|
||||
// set the value of global sql_auto_increment variable
|
||||
if (isset($_POST['sql_auto_increment'])) {
|
||||
$GLOBALS['sql_auto_increment'] = $_POST['sql_auto_increment'];
|
||||
}
|
||||
|
||||
$isView = (new Table($sourceTable, $sourceDb, $dbi))->isView();
|
||||
/**
|
||||
* The old structure of the table.
|
||||
*/
|
||||
$sqlStructure = $exportSqlPlugin->getTableDef($sourceDb, $sourceTable, false, false, $isView);
|
||||
|
||||
unset($noConstraintsComments);
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 0: Preparing structures used.
|
||||
|
||||
/**
|
||||
* The destination where the table is moved or copied to.
|
||||
*/
|
||||
$destination = new Expression($targetDb, $targetTable, '');
|
||||
|
||||
// Find server's SQL mode so the builder can generate correct
|
||||
// queries.
|
||||
// One of the options that alters the behaviour is `ANSI_QUOTES`.
|
||||
Context::setMode((string) $dbi->fetchValue('SELECT @@sql_mode'));
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 1: Dropping existent element of the same name (if exists
|
||||
// and required).
|
||||
|
||||
if ($addDropIfExists) {
|
||||
/**
|
||||
* Drop statement used for building the query.
|
||||
*/
|
||||
$statement = new DropStatement();
|
||||
|
||||
$tbl = new Table($targetTable, $targetDb, $dbi);
|
||||
|
||||
$statement->options = new OptionsArray(
|
||||
[$tbl->isView() ? 'VIEW' : 'TABLE', 'IF EXISTS'],
|
||||
);
|
||||
|
||||
$statement->fields = [$destination];
|
||||
|
||||
// Building the query.
|
||||
$dropQuery = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
$dbi->query($dropQuery);
|
||||
$GLOBALS['sql_query'] .= "\n" . $dropQuery;
|
||||
|
||||
// If an existing table gets deleted, maintain any entries for
|
||||
// the PMA_* tables.
|
||||
$maintainRelations = true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 2: Generating the new query of this structure.
|
||||
|
||||
/**
|
||||
* The parser responsible for parsing the old queries.
|
||||
*/
|
||||
$parser = new Parser($sqlStructure);
|
||||
|
||||
if (! empty($parser->statements[0])) {
|
||||
|
||||
/**
|
||||
* The CREATE statement of this structure.
|
||||
*
|
||||
* @var CreateStatement $statement
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
|
||||
// Changing the destination.
|
||||
$statement->name = $destination;
|
||||
|
||||
// Building back the query.
|
||||
$sqlStructure = $statement->build() . ';';
|
||||
|
||||
// This is to avoid some issues when renaming databases with views
|
||||
// See: https://github.com/phpmyadmin/phpmyadmin/issues/16422
|
||||
if ($move) {
|
||||
$dbi->selectDb($targetDb);
|
||||
}
|
||||
|
||||
// Executing it
|
||||
$dbi->query($sqlStructure);
|
||||
$GLOBALS['sql_query'] .= "\n" . $sqlStructure;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 3: Adding constraints.
|
||||
// All constraint names are removed because they must be unique.
|
||||
|
||||
if (($move || isset($GLOBALS['add_constraints'])) && ! empty($GLOBALS['sql_constraints_query'])) {
|
||||
$parser = new Parser($GLOBALS['sql_constraints_query']);
|
||||
|
||||
/**
|
||||
* The ALTER statement that generates the constraints.
|
||||
*
|
||||
* @var AlterStatement $statement
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Removing the name of the constraints.
|
||||
foreach ($statement->altered as $altered) {
|
||||
// All constraint names are removed because they must be unique.
|
||||
if (! $altered->options->has('CONSTRAINT')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$altered->field = null;
|
||||
}
|
||||
|
||||
// Building back the query.
|
||||
$GLOBALS['sql_constraints_query'] = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
if ($mode === 'one_table') {
|
||||
$dbi->query($GLOBALS['sql_constraints_query']);
|
||||
}
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_constraints_query'];
|
||||
if ($mode === 'one_table') {
|
||||
unset($GLOBALS['sql_constraints_query']);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 4: Adding indexes.
|
||||
// View phase 3.
|
||||
|
||||
if (! empty($GLOBALS['sql_indexes'])) {
|
||||
$parser = new Parser($GLOBALS['sql_indexes']);
|
||||
|
||||
$GLOBALS['sql_indexes'] = '';
|
||||
/**
|
||||
* The ALTER statement that generates the indexes.
|
||||
*
|
||||
* @var AlterStatement $statement
|
||||
*/
|
||||
foreach ($parser->statements as $statement) {
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Removing the name of the constraints.
|
||||
foreach ($statement->altered as $altered) {
|
||||
// All constraint names are removed because they must be unique.
|
||||
if (! $altered->options->has('CONSTRAINT')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$altered->field = null;
|
||||
}
|
||||
|
||||
// Building back the query.
|
||||
$sqlIndex = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
if ($mode === 'one_table' || $mode === 'db_copy') {
|
||||
$dbi->query($sqlIndex);
|
||||
}
|
||||
|
||||
$GLOBALS['sql_indexes'] .= $sqlIndex;
|
||||
}
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_indexes'];
|
||||
if ($mode === 'one_table' || $mode === 'db_copy') {
|
||||
unset($GLOBALS['sql_indexes']);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 5: Adding AUTO_INCREMENT.
|
||||
|
||||
if (! empty($GLOBALS['sql_auto_increments']) && ($mode === 'one_table' || $mode === 'db_copy')) {
|
||||
$parser = new Parser($GLOBALS['sql_auto_increments']);
|
||||
|
||||
/**
|
||||
* The ALTER statement that alters the AUTO_INCREMENT value.
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
if ($statement instanceof AlterStatement) {
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Building back the query.
|
||||
$GLOBALS['sql_auto_increments'] = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
$dbi->query($GLOBALS['sql_auto_increments']);
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_auto_increments'];
|
||||
}
|
||||
|
||||
unset($GLOBALS['sql_auto_increments']);
|
||||
}
|
||||
} else {
|
||||
$GLOBALS['sql_query'] = '';
|
||||
}
|
||||
|
||||
$table = new Table($targetTable, $targetDb, $dbi);
|
||||
// Copy the data unless this is a VIEW
|
||||
if (($what === 'data' || $what === 'dataonly') && ! $table->isView()) {
|
||||
$sqlSetMode = "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'";
|
||||
$dbi->query($sqlSetMode);
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlSetMode . ';';
|
||||
|
||||
$oldTable = new Table($sourceTable, $sourceDb, $dbi);
|
||||
$nonGeneratedCols = $oldTable->getNonGeneratedColumns();
|
||||
if ($nonGeneratedCols !== []) {
|
||||
$sqlInsertData = 'INSERT INTO ' . $target . '('
|
||||
. implode(', ', $nonGeneratedCols)
|
||||
. ') SELECT ' . implode(', ', $nonGeneratedCols)
|
||||
. ' FROM ' . $source;
|
||||
|
||||
$dbi->query($sqlInsertData);
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlInsertData . ';';
|
||||
}
|
||||
}
|
||||
|
||||
$relationParameters = $relation->getRelationParameters();
|
||||
|
||||
// Drops old table if the user has requested to move it
|
||||
if ($move) {
|
||||
// This could avoid some problems with replicated databases, when
|
||||
// moving table from replicated one to not replicated one
|
||||
$dbi->selectDb($sourceDb);
|
||||
|
||||
$sourceTableObj = new Table($sourceTable, $sourceDb, $dbi);
|
||||
$sqlDropQuery = $sourceTableObj->isView() ? 'DROP VIEW' : 'DROP TABLE';
|
||||
|
||||
$sqlDropQuery .= ' ' . $source;
|
||||
$dbi->query($sqlDropQuery);
|
||||
|
||||
// Rename table in configuration storage
|
||||
$relation->renameTable($sourceDb, $targetDb, $sourceTable, $targetTable);
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlDropQuery . ';';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// we are copying
|
||||
// Create new entries as duplicates from old PMA DBs
|
||||
if ($what === 'dataonly' || isset($maintainRelations)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($relationParameters->columnCommentsFeature !== null) {
|
||||
// Get all comments and MIME-Types for current table
|
||||
$commentsCopyRs = $dbi->queryAsControlUser(
|
||||
'SELECT column_name, comment'
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ', mimetype, transformation, transformation_options'
|
||||
: '')
|
||||
. ' FROM '
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->database)
|
||||
. '.'
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->columnInfo)
|
||||
. ' WHERE '
|
||||
. ' db_name = ' . $dbi->quoteString($sourceDb, ConnectionType::ControlUser)
|
||||
. ' AND '
|
||||
. ' table_name = ' . $dbi->quoteString($sourceTable, ConnectionType::ControlUser),
|
||||
);
|
||||
|
||||
// Write every comment as new copied entry. [MIME]
|
||||
foreach ($commentsCopyRs as $commentsCopyRow) {
|
||||
$newCommentQuery = 'REPLACE INTO '
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->database)
|
||||
. '.' . Util::backquote($relationParameters->columnCommentsFeature->columnInfo)
|
||||
. ' (db_name, table_name, column_name, comment'
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ', mimetype, transformation, transformation_options'
|
||||
: '')
|
||||
. ') VALUES(' . $dbi->quoteString($targetDb, ConnectionType::ControlUser)
|
||||
. ',' . $dbi->quoteString($targetTable, ConnectionType::ControlUser) . ','
|
||||
. $dbi->quoteString($commentsCopyRow['column_name'], ConnectionType::ControlUser)
|
||||
. ','
|
||||
. $dbi->quoteString($commentsCopyRow['comment'], ConnectionType::ControlUser)
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ',' . $dbi->quoteString($commentsCopyRow['mimetype'], ConnectionType::ControlUser)
|
||||
. ',' . $dbi->quoteString($commentsCopyRow['transformation'], ConnectionType::ControlUser)
|
||||
. ','
|
||||
. $dbi->quoteString($commentsCopyRow['transformation_options'], ConnectionType::ControlUser)
|
||||
: '')
|
||||
. ')';
|
||||
$dbi->queryAsControlUser($newCommentQuery);
|
||||
}
|
||||
|
||||
unset($commentsCopyRs);
|
||||
}
|
||||
|
||||
// duplicating the bookmarks must not be done here, but
|
||||
// just once per db
|
||||
|
||||
$getFields = ['display_field'];
|
||||
$whereFields = ['db_name' => $sourceDb, 'table_name' => $sourceTable];
|
||||
$newFields = ['db_name' => $targetDb, 'table_name' => $targetTable];
|
||||
self::duplicateInfo('displaywork', 'table_info', $getFields, $whereFields, $newFields);
|
||||
|
||||
/** @todo revise this code when we support cross-db relations */
|
||||
$getFields = ['master_field', 'foreign_table', 'foreign_field'];
|
||||
$whereFields = ['master_db' => $sourceDb, 'master_table' => $sourceTable];
|
||||
$newFields = ['master_db' => $targetDb, 'foreign_db' => $targetDb, 'master_table' => $targetTable];
|
||||
self::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
|
||||
$getFields = ['foreign_field', 'master_table', 'master_field'];
|
||||
$whereFields = ['foreign_db' => $sourceDb, 'foreign_table' => $sourceTable];
|
||||
$newFields = ['master_db' => $targetDb, 'foreign_db' => $targetDb, 'foreign_table' => $targetTable];
|
||||
self::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if given name is a valid table name,
|
||||
* currently if not empty, trailing spaces, '.', '/' and '\'
|
||||
|
||||
527
src/Table/TableMover.php
Normal file
527
src/Table/TableMover.php
Normal file
@ -0,0 +1,527 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Table;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\ConnectionType;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\Plugins\Export\ExportSql;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statements\AlterStatement;
|
||||
use PhpMyAdmin\SqlParser\Statements\CreateStatement;
|
||||
use PhpMyAdmin\SqlParser\Statements\DropStatement;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function htmlspecialchars;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
|
||||
class TableMover
|
||||
{
|
||||
/**
|
||||
* Copies or renames table
|
||||
*
|
||||
* @param string $sourceDb source database
|
||||
* @param string $sourceTable source table
|
||||
* @param string $targetDb target database
|
||||
* @param string $targetTable target table
|
||||
* @param string $what what to be moved or copied (data, dataonly)
|
||||
* @param bool $move whether to move
|
||||
* @param string $mode mode
|
||||
*/
|
||||
public static function moveCopy(
|
||||
string $sourceDb,
|
||||
string $sourceTable,
|
||||
string $targetDb,
|
||||
string $targetTable,
|
||||
string $what,
|
||||
bool $move,
|
||||
string $mode,
|
||||
bool $addDropIfExists,
|
||||
): bool {
|
||||
$GLOBALS['errorUrl'] ??= null;
|
||||
$dbi = DatabaseInterface::getInstance();
|
||||
$relation = new Relation($dbi);
|
||||
|
||||
// Try moving the tables directly, using native `RENAME` statement.
|
||||
if ($move && $what === 'data') {
|
||||
$tbl = new Table($sourceTable, $sourceDb, $dbi);
|
||||
if ($tbl->rename($targetTable, $targetDb)) {
|
||||
$GLOBALS['message'] = $tbl->getLastMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Setting required export settings.
|
||||
$GLOBALS['asfile'] = 1;
|
||||
|
||||
// Ensuring the target database is valid.
|
||||
$databaseList = $dbi->getDatabaseList();
|
||||
if (! $databaseList->exists($sourceDb, $targetDb)) {
|
||||
if (! $databaseList->exists($sourceDb)) {
|
||||
$GLOBALS['message'] = Message::rawError(
|
||||
sprintf(
|
||||
__('Source database `%s` was not found!'),
|
||||
htmlspecialchars($sourceDb),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (! $databaseList->exists($targetDb)) {
|
||||
$GLOBALS['message'] = Message::rawError(
|
||||
sprintf(
|
||||
__('Target database `%s` was not found!'),
|
||||
htmlspecialchars($targetDb),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full name of source table, quoted.
|
||||
*/
|
||||
$source = Util::backquote($sourceDb) . '.' . Util::backquote($sourceTable);
|
||||
|
||||
// If the target database is not specified, the operation is taking
|
||||
// place in the same database.
|
||||
if ($targetDb === '') {
|
||||
$targetDb = $sourceDb;
|
||||
}
|
||||
|
||||
// Selecting the database could avoid some problems with replicated
|
||||
// databases, when moving table from replicated one to not replicated one.
|
||||
$dbi->selectDb($targetDb);
|
||||
|
||||
/**
|
||||
* The full name of target table, quoted.
|
||||
*/
|
||||
$target = Util::backquote($targetDb) . '.' . Util::backquote($targetTable);
|
||||
|
||||
// No table is created when this is a data-only operation.
|
||||
if ($what !== 'dataonly') {
|
||||
/**
|
||||
* Instance used for exporting the current structure of the table.
|
||||
*
|
||||
* @var ExportSql $exportSqlPlugin
|
||||
*/
|
||||
$exportSqlPlugin = Plugins::getPlugin('export', 'sql', [
|
||||
'export_type' => 'table',
|
||||
'single_table' => false,
|
||||
]);
|
||||
// It is better that all identifiers are quoted
|
||||
$exportSqlPlugin->useSqlBackquotes(true);
|
||||
|
||||
$noConstraintsComments = true;
|
||||
$GLOBALS['sql_constraints_query'] = '';
|
||||
// set the value of global sql_auto_increment variable
|
||||
if (isset($_POST['sql_auto_increment'])) {
|
||||
$GLOBALS['sql_auto_increment'] = $_POST['sql_auto_increment'];
|
||||
}
|
||||
|
||||
$isView = (new Table($sourceTable, $sourceDb, $dbi))->isView();
|
||||
/**
|
||||
* The old structure of the table.
|
||||
*/
|
||||
$sqlStructure = $exportSqlPlugin->getTableDef($sourceDb, $sourceTable, false, false, $isView);
|
||||
|
||||
unset($noConstraintsComments);
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 0: Preparing structures used.
|
||||
|
||||
/**
|
||||
* The destination where the table is moved or copied to.
|
||||
*/
|
||||
$destination = new Expression($targetDb, $targetTable, '');
|
||||
|
||||
// Find server's SQL mode so the builder can generate correct
|
||||
// queries.
|
||||
// One of the options that alters the behaviour is `ANSI_QUOTES`.
|
||||
Context::setMode((string) $dbi->fetchValue('SELECT @@sql_mode'));
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 1: Dropping existent element of the same name (if exists
|
||||
// and required).
|
||||
|
||||
if ($addDropIfExists) {
|
||||
/**
|
||||
* Drop statement used for building the query.
|
||||
*/
|
||||
$statement = new DropStatement();
|
||||
|
||||
$tbl = new Table($targetTable, $targetDb, $dbi);
|
||||
|
||||
$statement->options = new OptionsArray(
|
||||
[$tbl->isView() ? 'VIEW' : 'TABLE', 'IF EXISTS'],
|
||||
);
|
||||
|
||||
$statement->fields = [$destination];
|
||||
|
||||
// Building the query.
|
||||
$dropQuery = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
$dbi->query($dropQuery);
|
||||
$GLOBALS['sql_query'] .= "\n" . $dropQuery;
|
||||
|
||||
// If an existing table gets deleted, maintain any entries for
|
||||
// the PMA_* tables.
|
||||
$maintainRelations = true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 2: Generating the new query of this structure.
|
||||
|
||||
/**
|
||||
* The parser responsible for parsing the old queries.
|
||||
*/
|
||||
$parser = new Parser($sqlStructure);
|
||||
|
||||
if (! empty($parser->statements[0])) {
|
||||
|
||||
/**
|
||||
* The CREATE statement of this structure.
|
||||
*
|
||||
* @var CreateStatement $statement
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
|
||||
// Changing the destination.
|
||||
$statement->name = $destination;
|
||||
|
||||
// Building back the query.
|
||||
$sqlStructure = $statement->build() . ';';
|
||||
|
||||
// This is to avoid some issues when renaming databases with views
|
||||
// See: https://github.com/phpmyadmin/phpmyadmin/issues/16422
|
||||
if ($move) {
|
||||
$dbi->selectDb($targetDb);
|
||||
}
|
||||
|
||||
// Executing it
|
||||
$dbi->query($sqlStructure);
|
||||
$GLOBALS['sql_query'] .= "\n" . $sqlStructure;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 3: Adding constraints.
|
||||
// All constraint names are removed because they must be unique.
|
||||
|
||||
if (($move || isset($GLOBALS['add_constraints'])) && ! empty($GLOBALS['sql_constraints_query'])) {
|
||||
$parser = new Parser($GLOBALS['sql_constraints_query']);
|
||||
|
||||
/**
|
||||
* The ALTER statement that generates the constraints.
|
||||
*
|
||||
* @var AlterStatement $statement
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Removing the name of the constraints.
|
||||
foreach ($statement->altered as $altered) {
|
||||
// All constraint names are removed because they must be unique.
|
||||
if (! $altered->options->has('CONSTRAINT')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$altered->field = null;
|
||||
}
|
||||
|
||||
// Building back the query.
|
||||
$GLOBALS['sql_constraints_query'] = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
if ($mode === 'one_table') {
|
||||
$dbi->query($GLOBALS['sql_constraints_query']);
|
||||
}
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_constraints_query'];
|
||||
if ($mode === 'one_table') {
|
||||
unset($GLOBALS['sql_constraints_query']);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 4: Adding indexes.
|
||||
// View phase 3.
|
||||
|
||||
if (! empty($GLOBALS['sql_indexes'])) {
|
||||
$parser = new Parser($GLOBALS['sql_indexes']);
|
||||
|
||||
$GLOBALS['sql_indexes'] = '';
|
||||
/**
|
||||
* The ALTER statement that generates the indexes.
|
||||
*
|
||||
* @var AlterStatement $statement
|
||||
*/
|
||||
foreach ($parser->statements as $statement) {
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Removing the name of the constraints.
|
||||
foreach ($statement->altered as $altered) {
|
||||
// All constraint names are removed because they must be unique.
|
||||
if (! $altered->options->has('CONSTRAINT')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$altered->field = null;
|
||||
}
|
||||
|
||||
// Building back the query.
|
||||
$sqlIndex = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
if ($mode === 'one_table' || $mode === 'db_copy') {
|
||||
$dbi->query($sqlIndex);
|
||||
}
|
||||
|
||||
$GLOBALS['sql_indexes'] .= $sqlIndex;
|
||||
}
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_indexes'];
|
||||
if ($mode === 'one_table' || $mode === 'db_copy') {
|
||||
unset($GLOBALS['sql_indexes']);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Phase 5: Adding AUTO_INCREMENT.
|
||||
|
||||
if (! empty($GLOBALS['sql_auto_increments']) && ($mode === 'one_table' || $mode === 'db_copy')) {
|
||||
$parser = new Parser($GLOBALS['sql_auto_increments']);
|
||||
|
||||
/**
|
||||
* The ALTER statement that alters the AUTO_INCREMENT value.
|
||||
*/
|
||||
$statement = $parser->statements[0];
|
||||
if ($statement instanceof AlterStatement) {
|
||||
// Changing the altered table to the destination.
|
||||
$statement->table = $destination;
|
||||
|
||||
// Building back the query.
|
||||
$GLOBALS['sql_auto_increments'] = $statement->build() . ';';
|
||||
|
||||
// Executing it.
|
||||
$dbi->query($GLOBALS['sql_auto_increments']);
|
||||
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_auto_increments'];
|
||||
}
|
||||
|
||||
unset($GLOBALS['sql_auto_increments']);
|
||||
}
|
||||
} else {
|
||||
$GLOBALS['sql_query'] = '';
|
||||
}
|
||||
|
||||
$table = new Table($targetTable, $targetDb, $dbi);
|
||||
// Copy the data unless this is a VIEW
|
||||
if (($what === 'data' || $what === 'dataonly') && ! $table->isView()) {
|
||||
$sqlSetMode = "SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'";
|
||||
$dbi->query($sqlSetMode);
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlSetMode . ';';
|
||||
|
||||
$oldTable = new Table($sourceTable, $sourceDb, $dbi);
|
||||
$nonGeneratedCols = $oldTable->getNonGeneratedColumns();
|
||||
if ($nonGeneratedCols !== []) {
|
||||
$sqlInsertData = 'INSERT INTO ' . $target . '('
|
||||
. implode(', ', $nonGeneratedCols)
|
||||
. ') SELECT ' . implode(', ', $nonGeneratedCols)
|
||||
. ' FROM ' . $source;
|
||||
|
||||
$dbi->query($sqlInsertData);
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlInsertData . ';';
|
||||
}
|
||||
}
|
||||
|
||||
$relationParameters = $relation->getRelationParameters();
|
||||
|
||||
// Drops old table if the user has requested to move it
|
||||
if ($move) {
|
||||
// This could avoid some problems with replicated databases, when
|
||||
// moving table from replicated one to not replicated one
|
||||
$dbi->selectDb($sourceDb);
|
||||
|
||||
$sourceTableObj = new Table($sourceTable, $sourceDb, $dbi);
|
||||
$sqlDropQuery = $sourceTableObj->isView() ? 'DROP VIEW' : 'DROP TABLE';
|
||||
|
||||
$sqlDropQuery .= ' ' . $source;
|
||||
$dbi->query($sqlDropQuery);
|
||||
|
||||
// Rename table in configuration storage
|
||||
$relation->renameTable($sourceDb, $targetDb, $sourceTable, $targetTable);
|
||||
|
||||
$GLOBALS['sql_query'] .= "\n\n" . $sqlDropQuery . ';';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// we are copying
|
||||
// Create new entries as duplicates from old PMA DBs
|
||||
if ($what === 'dataonly' || isset($maintainRelations)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($relationParameters->columnCommentsFeature !== null) {
|
||||
// Get all comments and MIME-Types for current table
|
||||
$commentsCopyRs = $dbi->queryAsControlUser(
|
||||
'SELECT column_name, comment'
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ', mimetype, transformation, transformation_options'
|
||||
: '')
|
||||
. ' FROM '
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->database)
|
||||
. '.'
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->columnInfo)
|
||||
. ' WHERE '
|
||||
. ' db_name = ' . $dbi->quoteString($sourceDb, ConnectionType::ControlUser)
|
||||
. ' AND '
|
||||
. ' table_name = ' . $dbi->quoteString($sourceTable, ConnectionType::ControlUser),
|
||||
);
|
||||
|
||||
// Write every comment as new copied entry. [MIME]
|
||||
foreach ($commentsCopyRs as $commentsCopyRow) {
|
||||
$newCommentQuery = 'REPLACE INTO '
|
||||
. Util::backquote($relationParameters->columnCommentsFeature->database)
|
||||
. '.' . Util::backquote($relationParameters->columnCommentsFeature->columnInfo)
|
||||
. ' (db_name, table_name, column_name, comment'
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ', mimetype, transformation, transformation_options'
|
||||
: '')
|
||||
. ') VALUES(' . $dbi->quoteString($targetDb, ConnectionType::ControlUser)
|
||||
. ',' . $dbi->quoteString($targetTable, ConnectionType::ControlUser) . ','
|
||||
. $dbi->quoteString($commentsCopyRow['column_name'], ConnectionType::ControlUser)
|
||||
. ','
|
||||
. $dbi->quoteString($commentsCopyRow['comment'], ConnectionType::ControlUser)
|
||||
. ($relationParameters->browserTransformationFeature !== null
|
||||
? ',' . $dbi->quoteString($commentsCopyRow['mimetype'], ConnectionType::ControlUser)
|
||||
. ',' . $dbi->quoteString($commentsCopyRow['transformation'], ConnectionType::ControlUser)
|
||||
. ','
|
||||
. $dbi->quoteString($commentsCopyRow['transformation_options'], ConnectionType::ControlUser)
|
||||
: '')
|
||||
. ')';
|
||||
$dbi->queryAsControlUser($newCommentQuery);
|
||||
}
|
||||
|
||||
unset($commentsCopyRs);
|
||||
}
|
||||
|
||||
// duplicating the bookmarks must not be done here, but
|
||||
// just once per db
|
||||
|
||||
$getFields = ['display_field'];
|
||||
$whereFields = ['db_name' => $sourceDb, 'table_name' => $sourceTable];
|
||||
$newFields = ['db_name' => $targetDb, 'table_name' => $targetTable];
|
||||
self::duplicateInfo('displaywork', 'table_info', $getFields, $whereFields, $newFields);
|
||||
|
||||
/** @todo revise this code when we support cross-db relations */
|
||||
$getFields = ['master_field', 'foreign_table', 'foreign_field'];
|
||||
$whereFields = ['master_db' => $sourceDb, 'master_table' => $sourceTable];
|
||||
$newFields = ['master_db' => $targetDb, 'foreign_db' => $targetDb, 'master_table' => $targetTable];
|
||||
self::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
|
||||
$getFields = ['foreign_field', 'master_table', 'master_field'];
|
||||
$whereFields = ['foreign_db' => $sourceDb, 'foreign_table' => $sourceTable];
|
||||
$newFields = ['master_db' => $targetDb, 'foreign_db' => $targetDb, 'foreign_table' => $targetTable];
|
||||
self::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts existing entries in a PMA_* table by reading a value from an old
|
||||
* entry
|
||||
*
|
||||
* @param string $work The array index, which Relation feature to check ('relwork', 'commwork', ...)
|
||||
* @param string $table The array index, which PMA-table to update ('bookmark', 'relation', ...)
|
||||
* @param string[] $getFields Which fields will be SELECT'ed from the old entry
|
||||
* @param mixed[] $whereFields Which fields will be used for the WHERE query (array('FIELDNAME' => 'FIELDVALUE'))
|
||||
* @param mixed[] $newFields Which fields will be used as new VALUES. These are the important keys which differ
|
||||
* from the old entry (array('FIELDNAME' => 'NEW FIELDVALUE'))
|
||||
*/
|
||||
public static function duplicateInfo(
|
||||
string $work,
|
||||
string $table,
|
||||
array $getFields,
|
||||
array $whereFields,
|
||||
array $newFields,
|
||||
): int|bool {
|
||||
$dbi = DatabaseInterface::getInstance();
|
||||
$relation = new Relation($dbi);
|
||||
$relationParameters = $relation->getRelationParameters();
|
||||
$relationParams = $relationParameters->toArray();
|
||||
$lastId = -1;
|
||||
|
||||
if (! isset($relationParams[$work], $relationParams[$table]) || ! $relationParams[$work]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$selectParts = [];
|
||||
$rowFields = [];
|
||||
foreach ($getFields as $getField) {
|
||||
$selectParts[] = Util::backquote($getField);
|
||||
$rowFields[] = $getField;
|
||||
}
|
||||
|
||||
$whereParts = [];
|
||||
foreach ($whereFields as $where => $value) {
|
||||
$whereParts[] = Util::backquote((string) $where) . ' = '
|
||||
. $dbi->quoteString((string) $value, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$newParts = [];
|
||||
$newValueParts = [];
|
||||
foreach ($newFields as $where => $value) {
|
||||
$newParts[] = Util::backquote((string) $where);
|
||||
$newValueParts[] = $dbi->quoteString((string) $value, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$tableCopyQuery = '
|
||||
SELECT ' . implode(', ', $selectParts) . '
|
||||
FROM ' . Util::backquote($relationParameters->db) . '.'
|
||||
. Util::backquote((string) $relationParams[$table]) . '
|
||||
WHERE ' . implode(' AND ', $whereParts);
|
||||
|
||||
// must use DatabaseInterface::QUERY_BUFFERED here, since we execute
|
||||
// another query inside the loop
|
||||
$tableCopyRs = $dbi->queryAsControlUser($tableCopyQuery);
|
||||
|
||||
foreach ($tableCopyRs as $tableCopyRow) {
|
||||
$valueParts = [];
|
||||
foreach ($tableCopyRow as $key => $val) {
|
||||
if (! in_array($key, $rowFields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueParts[] = $dbi->quoteString($val, ConnectionType::ControlUser);
|
||||
}
|
||||
|
||||
$newTableQuery = 'INSERT IGNORE INTO '
|
||||
. Util::backquote($relationParameters->db)
|
||||
. '.' . Util::backquote((string) $relationParams[$table])
|
||||
. ' (' . implode(', ', $selectParts) . ', '
|
||||
. implode(', ', $newParts) . ') VALUES ('
|
||||
. implode(', ', $valueParts) . ', '
|
||||
. implode(', ', $newValueParts) . ')';
|
||||
|
||||
$dbi->queryAsControlUser($newTableQuery);
|
||||
$lastId = $dbi->insertId();
|
||||
}
|
||||
|
||||
return $lastId;
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ use PhpMyAdmin\ListDatabase;
|
||||
use PhpMyAdmin\Query\Cache;
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
use PhpMyAdmin\Table\Table;
|
||||
use PhpMyAdmin\Table\TableMover;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
use PhpMyAdmin\Tests\FieldHelper;
|
||||
use PhpMyAdmin\Tests\Stubs\DbiDummy;
|
||||
@ -898,7 +899,7 @@ class TableTest extends AbstractTestCase
|
||||
]);
|
||||
(new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
|
||||
|
||||
$ret = Table::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
$ret = TableMover::duplicateInfo('relwork', 'relation', $getFields, $whereFields, $newFields);
|
||||
self::assertSame(-1, $ret);
|
||||
}
|
||||
|
||||
@ -1322,7 +1323,7 @@ class TableTest extends AbstractTestCase
|
||||
$this->mockedDbi->expects(self::any())->method('getTable')
|
||||
->willReturnMap($getTableMap);
|
||||
|
||||
$return = Table::moveCopy($sourceDb, $sourceTable, $targetDb, $targetTable, $what, $move, $mode, true);
|
||||
$return = TableMover::moveCopy($sourceDb, $sourceTable, $targetDb, $targetTable, $what, $move, $mode, true);
|
||||
|
||||
//successfully
|
||||
self::assertTrue($return);
|
||||
@ -1333,7 +1334,7 @@ class TableTest extends AbstractTestCase
|
||||
$sqlQuery = 'DROP VIEW `PMA`.`PMA_BookMark`';
|
||||
self::assertStringContainsString($sqlQuery, $GLOBALS['sql_query']);
|
||||
|
||||
$return = Table::moveCopy($sourceDb, $sourceTable, $targetDb, $targetTable, $what, false, $mode, true);
|
||||
$return = TableMover::moveCopy($sourceDb, $sourceTable, $targetDb, $targetTable, $what, false, $mode, true);
|
||||
|
||||
//successfully
|
||||
self::assertTrue($return);
|
||||
@ -1375,7 +1376,7 @@ class TableTest extends AbstractTestCase
|
||||
]);
|
||||
|
||||
$GLOBALS['sql_query'] = '';
|
||||
$return = Table::moveCopy('aa', 'ad', 'bb', 'ad', 'structure', true, 'db_copy', true);
|
||||
$return = TableMover::moveCopy('aa', 'ad', 'bb', 'ad', 'structure', true, 'db_copy', true);
|
||||
self::assertTrue($return);
|
||||
self::assertStringContainsString('DROP TABLE IF EXISTS `bb`.`ad`;', $GLOBALS['sql_query']);
|
||||
self::assertStringContainsString(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user