1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765 |
- <?php
- declare(strict_types=1);
- namespace PhpMyAdmin\Controllers\Table;
- use PhpMyAdmin\Charsets;
- use PhpMyAdmin\CheckUserPrivileges;
- use PhpMyAdmin\Config\PageSettings;
- use PhpMyAdmin\Controllers\SqlController;
- use PhpMyAdmin\Core;
- use PhpMyAdmin\CreateAddField;
- use PhpMyAdmin\Database\CentralColumns;
- use PhpMyAdmin\DatabaseInterface;
- use PhpMyAdmin\DbTableExists;
- use PhpMyAdmin\Engines\Innodb;
- use PhpMyAdmin\Html\Generator;
- use PhpMyAdmin\Index;
- use PhpMyAdmin\Message;
- use PhpMyAdmin\Operations;
- use PhpMyAdmin\ParseAnalyze;
- use PhpMyAdmin\Partition;
- use PhpMyAdmin\Query\Utilities;
- use PhpMyAdmin\Relation;
- use PhpMyAdmin\RelationCleanup;
- use PhpMyAdmin\Response;
- use PhpMyAdmin\Sql;
- use PhpMyAdmin\SqlParser\Context;
- use PhpMyAdmin\SqlParser\Parser;
- use PhpMyAdmin\SqlParser\Statements\CreateStatement;
- use PhpMyAdmin\StorageEngine;
- use PhpMyAdmin\Table;
- use PhpMyAdmin\Table\ColumnsDefinition;
- use PhpMyAdmin\TablePartitionDefinition;
- use PhpMyAdmin\Template;
- use PhpMyAdmin\Tracker;
- use PhpMyAdmin\Transformations;
- use PhpMyAdmin\Url;
- use PhpMyAdmin\Util;
- use stdClass;
- use function array_keys;
- use function array_splice;
- use function count;
- use function implode;
- use function in_array;
- use function is_array;
- use function is_string;
- use function mb_strpos;
- use function mb_strtoupper;
- use function sprintf;
- use function str_replace;
- use function strlen;
- use function strpos;
- use function strrpos;
- use function substr;
- use function trim;
- /**
- * Displays table structure infos like columns, indexes, size, rows
- * and allows manipulation of indexes and columns.
- */
- class StructureController extends AbstractController
- {
- /** @var Table The table object */
- protected $tableObj;
- /** @var CreateAddField */
- private $createAddField;
- /** @var Relation */
- private $relation;
- /** @var Transformations */
- private $transformations;
- /** @var RelationCleanup */
- private $relationCleanup;
- /** @var DatabaseInterface */
- private $dbi;
- /**
- * @param Response $response
- * @param string $db Database name
- * @param string $table Table name
- * @param DatabaseInterface $dbi
- */
- public function __construct(
- $response,
- Template $template,
- $db,
- $table,
- Relation $relation,
- Transformations $transformations,
- CreateAddField $createAddField,
- RelationCleanup $relationCleanup,
- $dbi
- ) {
- parent::__construct($response, $template, $db, $table);
- $this->createAddField = $createAddField;
- $this->relation = $relation;
- $this->transformations = $transformations;
- $this->relationCleanup = $relationCleanup;
- $this->dbi = $dbi;
- $this->tableObj = $this->dbi->getTable($this->db, $this->table);
- }
- public function index(): void
- {
- global $reread_info, $showtable, $db, $table, $cfg, $err_url;
- global $tbl_is_view, $tbl_storage_engine, $tbl_collation, $table_info_num_rows;
- $this->dbi->selectDb($this->db);
- $reread_info = $this->tableObj->getStatusInfo(null, true);
- $showtable = $this->tableObj->getStatusInfo(
- null,
- (isset($reread_info) && $reread_info)
- );
- if ($this->tableObj->isView()) {
- $tbl_is_view = true;
- $tbl_storage_engine = __('View');
- } else {
- $tbl_is_view = false;
- $tbl_storage_engine = $this->tableObj->getStorageEngine();
- }
- $tbl_collation = $this->tableObj->getCollation();
- $table_info_num_rows = $this->tableObj->getNumRows();
- $pageSettings = new PageSettings('TableStructure');
- $this->response->addHTML($pageSettings->getErrorHTML());
- $this->response->addHTML($pageSettings->getHTML());
- $checkUserPrivileges = new CheckUserPrivileges($this->dbi);
- $checkUserPrivileges->getPrivileges();
- $this->addScriptFiles(['table/structure.js', 'indexes.js']);
- $cfgRelation = $this->relation->getRelationsParam();
- Util::checkParameters(['db', 'table']);
- $isSystemSchema = Utilities::isSystemSchema($db);
- $url_params = ['db' => $db, 'table' => $table];
- $err_url = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
- $err_url .= Url::getCommon($url_params, '&');
- DbTableExists::check();
- $primary = Index::getPrimary($this->table, $this->db);
- $columns_with_index = $this->dbi
- ->getTable($this->db, $this->table)
- ->getColumnsWithIndex(
- Index::UNIQUE | Index::INDEX | Index::SPATIAL
- | Index::FULLTEXT
- );
- $columns_with_unique_index = $this->dbi
- ->getTable($this->db, $this->table)
- ->getColumnsWithIndex(Index::UNIQUE);
- $fields = (array) $this->dbi->getColumns(
- $this->db,
- $this->table,
- null,
- true
- );
- $this->response->addHTML($this->displayStructure(
- $cfgRelation,
- $columns_with_unique_index,
- $primary,
- $fields,
- $columns_with_index,
- $isSystemSchema
- ));
- }
- public function save(): void
- {
- $regenerate = $this->updateColumns();
- if (! $regenerate) {
- // continue to show the table's structure
- unset($_POST['selected']);
- }
- $this->index();
- }
- public function addKey(): void
- {
- global $containerBuilder, $reload;
- /** @var SqlController $controller */
- $controller = $containerBuilder->get(SqlController::class);
- $controller->index();
- $reload = true;
- $this->index();
- }
- public function browse(): void
- {
- global $PMA_Theme;
- if (empty($_POST['selected_fld'])) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $this->displayTableBrowseForSelectedColumns(
- $GLOBALS['goto'],
- $PMA_Theme->getImgPath()
- );
- }
- public function change(): void
- {
- if (isset($_GET['change_column'])) {
- $this->displayHtmlForColumnChange(null);
- return;
- }
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $this->displayHtmlForColumnChange($selected);
- }
- public function addToCentralColumns(): void
- {
- global $sql_query, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $centralColumns = new CentralColumns($this->dbi);
- $centralColsError = $centralColumns->syncUniqueColumns(
- $selected,
- false
- );
- if ($centralColsError instanceof Message) {
- $message = $centralColsError;
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function removeFromCentralColumns(): void
- {
- global $sql_query, $db, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $centralColumns = new CentralColumns($this->dbi);
- $centralColsError = $centralColumns->deleteColumnsFromList(
- $db,
- $selected,
- false
- );
- if ($centralColsError instanceof Message) {
- $message = $centralColsError;
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function fulltext(): void
- {
- global $sql_query, $db, $table, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $i = 1;
- $selectedCount = count($selected);
- $sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD FULLTEXT(';
- foreach ($selected as $field) {
- $sql_query .= Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ');' : ', ';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function spatial(): void
- {
- global $sql_query, $db, $table, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $i = 1;
- $selectedCount = count($selected);
- $sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD SPATIAL(';
- foreach ($selected as $field) {
- $sql_query .= Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ');' : ', ';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function unique(): void
- {
- global $sql_query, $db, $table, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $i = 1;
- $selectedCount = count($selected);
- $sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD UNIQUE(';
- foreach ($selected as $field) {
- $sql_query .= Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ');' : ', ';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function addIndex(): void
- {
- global $sql_query, $db, $table, $message;
- $selected = $_POST['selected_fld'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $i = 1;
- $selectedCount = count($selected);
- $sql_query = 'ALTER TABLE ' . Util::backquote($table) . ' ADD INDEX(';
- foreach ($selected as $field) {
- $sql_query .= Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ');' : ', ';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function primary(): void
- {
- global $db, $table, $message, $sql_query, $url_params, $err_url, $cfg;
- $selected = $_POST['selected'] ?? [];
- $selected_fld = $_POST['selected_fld'] ?? [];
- if (empty($selected) && empty($selected_fld)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $primary = $this->getKeyForTablePrimary();
- if (empty($primary) && ! empty($selected_fld)) {
- // no primary key, so we can safely create new
- $mult_btn = __('Yes');
- $selected = $selected_fld;
- }
- $mult_btn = $_POST['mult_btn'] ?? $mult_btn ?? '';
- if (! empty($selected_fld) && ! empty($primary)) {
- Util::checkParameters(['db', 'table']);
- $url_params = ['db' => $db, 'table' => $table];
- $err_url = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
- $err_url .= Url::getCommon($url_params, '&');
- DbTableExists::check();
- $this->render('table/structure/primary', [
- 'db' => $db,
- 'table' => $table,
- 'selected' => $selected_fld,
- ]);
- return;
- }
- if ($mult_btn === __('Yes')) {
- $sql_query = 'ALTER TABLE ' . Util::backquote($table);
- if (! empty($primary)) {
- $sql_query .= ' DROP PRIMARY KEY,';
- }
- $sql_query .= ' ADD PRIMARY KEY(';
- $i = 1;
- $selectedCount = count($selected);
- foreach ($selected as $field) {
- $sql_query .= Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ');' : ', ';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function drop(): void
- {
- global $db, $table, $message, $sql_query;
- $selected = $_POST['selected'] ?? [];
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- $sql_query = '';
- if (($_POST['mult_btn'] ?? '') === __('Yes')) {
- $i = 1;
- $selectedCount = count($selected);
- $sql_query = 'ALTER TABLE ' . Util::backquote($table);
- foreach ($selected as $field) {
- $this->relationCleanup->column($db, $table, $field);
- $sql_query .= ' DROP ' . Util::backquote($field);
- $sql_query .= $i++ === $selectedCount ? ';' : ',';
- }
- $this->dbi->selectDb($db);
- $result = $this->dbi->tryQuery($sql_query);
- if (! $result) {
- $message = Message::error((string) $this->dbi->getError());
- }
- }
- if (empty($message)) {
- $message = Message::success();
- }
- $this->index();
- }
- public function dropConfirm(): void
- {
- global $db, $table, $url_params, $err_url, $cfg;
- $selected = $_POST['selected_fld'] ?? null;
- if (empty($selected)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
- return;
- }
- Util::checkParameters(['db', 'table']);
- $url_params = ['db' => $db, 'table' => $table];
- $err_url = Util::getScriptNameForOption($cfg['DefaultTabTable'], 'table');
- $err_url .= Url::getCommon($url_params, '&');
- DbTableExists::check();
- $this->render('table/structure/drop_confirm', [
- 'db' => $db,
- 'table' => $table,
- 'fields' => $selected,
- ]);
- }
- /**
- * Moves columns in the table's structure based on $_REQUEST
- */
- public function moveColumns(): void
- {
- if (! isset($_POST['move_columns'])
- || ! is_array($_POST['move_columns'])
- || ! $this->response->isAjax()
- ) {
- return;
- }
- $this->dbi->selectDb($this->db);
- /**
- * load the definitions for all columns
- */
- $columns = $this->dbi->getColumnsFull($this->db, $this->table);
- $column_names = array_keys($columns);
- $changes = [];
- // @see https://mariadb.com/kb/en/library/changes-improvements-in-mariadb-102/#information-schema
- $usesLiteralNull = $this->dbi->isMariaDB() && $this->dbi->getVersion() >= 100200;
- $defaultNullValue = $usesLiteralNull ? 'NULL' : null;
- // move columns from first to last
- for ($i = 0, $l = count($_POST['move_columns']); $i < $l; $i++) {
- $column = $_POST['move_columns'][$i];
- // is this column already correctly placed?
- if ($column_names[$i] == $column) {
- continue;
- }
- // it is not, let's move it to index $i
- $data = $columns[$column];
- $extracted_columnspec = Util::extractColumnSpec($data['Type']);
- if (isset($data['Extra'])
- && $data['Extra'] === 'on update CURRENT_TIMESTAMP'
- ) {
- $extracted_columnspec['attribute'] = $data['Extra'];
- unset($data['Extra']);
- }
- $timeType = $data['Type'] === 'timestamp' || $data['Type'] === 'datetime';
- $timeDefault = $data['Default'] === 'CURRENT_TIMESTAMP' || $data['Default'] === 'current_timestamp()';
- $current_timestamp = $timeType && $timeDefault;
- // @see https://mariadb.com/kb/en/library/information-schema-columns-table/#examples
- if ($data['Null'] === 'YES' && in_array($data['Default'], [$defaultNullValue, null])) {
- $default_type = 'NULL';
- } elseif ($current_timestamp) {
- $default_type = 'CURRENT_TIMESTAMP';
- } elseif ($data['Default'] === null) {
- $default_type = 'NONE';
- } else {
- $default_type = 'USER_DEFINED';
- }
- $virtual = [
- 'VIRTUAL',
- 'PERSISTENT',
- 'VIRTUAL GENERATED',
- 'STORED GENERATED',
- ];
- $data['Virtuality'] = '';
- $data['Expression'] = '';
- if (isset($data['Extra']) && in_array($data['Extra'], $virtual)) {
- $data['Virtuality'] = str_replace(' GENERATED', '', $data['Extra']);
- $expressions = $this->tableObj->getColumnGenerationExpression($column);
- $data['Expression'] = is_array($expressions) ? $expressions[$column] : null;
- }
- $changes[] = 'CHANGE ' . Table::generateAlter(
- $column,
- $column,
- mb_strtoupper($extracted_columnspec['type']),
- $extracted_columnspec['spec_in_brackets'],
- $extracted_columnspec['attribute'],
- $data['Collation'] ?? '',
- $data['Null'] === 'YES' ? 'YES' : 'NO',
- $default_type,
- $current_timestamp ? '' : $data['Default'],
- isset($data['Extra']) && $data['Extra'] !== '' ? $data['Extra']
- : false,
- isset($data['COLUMN_COMMENT']) && $data['COLUMN_COMMENT'] !== ''
- ? $data['COLUMN_COMMENT'] : false,
- $data['Virtuality'],
- $data['Expression'],
- $i === 0 ? '-first' : $column_names[$i - 1]
- );
- // update current column_names array, first delete old position
- for ($j = 0, $ll = count($column_names); $j < $ll; $j++) {
- if ($column_names[$j] != $column) {
- continue;
- }
- unset($column_names[$j]);
- }
- // insert moved column
- array_splice($column_names, $i, 0, $column);
- }
- if (empty($changes) && ! isset($_REQUEST['preview_sql'])) { // should never happen
- $this->response->setRequestStatus(false);
- return;
- }
- // query for moving the columns
- $sql_query = sprintf(
- 'ALTER TABLE %s %s',
- Util::backquote($this->table),
- implode(', ', $changes)
- );
- if (isset($_REQUEST['preview_sql'])) { // preview sql
- $this->response->addJSON(
- 'sql_data',
- $this->template->render('preview_sql', ['query_data' => $sql_query])
- );
- } else { // move column
- $this->dbi->tryQuery($sql_query);
- $tmp_error = $this->dbi->getError();
- if (is_string($tmp_error)) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', Message::error($tmp_error));
- } else {
- $message = Message::success(
- __('The columns have been moved successfully.')
- );
- $this->response->addJSON('message', $message);
- $this->response->addJSON('columns', $column_names);
- }
- }
- }
- /**
- * Displays HTML for changing one or more columns
- *
- * @param array|null $selected the selected columns
- */
- protected function displayHtmlForColumnChange($selected): void
- {
- global $action, $num_fields;
- if (empty($selected)) {
- $selected[] = $_REQUEST['field'];
- $selected_cnt = 1;
- } else { // from a multiple submit
- $selected_cnt = count($selected);
- }
- /**
- * @todo optimize in case of multiple fields to modify
- */
- $fields_meta = [];
- for ($i = 0; $i < $selected_cnt; $i++) {
- $value = $this->dbi->getColumns(
- $this->db,
- $this->table,
- $selected[$i],
- true
- );
- if (count($value) === 0) {
- $message = Message::error(
- __('Failed to get description of column %s!')
- );
- $message->addParam($selected[$i]);
- $this->response->addHTML($message);
- } else {
- $fields_meta[] = $value;
- }
- }
- $num_fields = count($fields_meta);
- $action = Url::getFromRoute('/table/structure/save');
- /**
- * Form for changing properties.
- */
- $checkUserPrivileges = new CheckUserPrivileges($this->dbi);
- $checkUserPrivileges->getPrivileges();
- $this->addScriptFiles(['vendor/jquery/jquery.uitablefilter.js', 'indexes.js']);
- $templateData = ColumnsDefinition::displayForm(
- $this->transformations,
- $this->relation,
- $this->dbi,
- $action,
- $num_fields,
- null,
- $selected,
- $fields_meta
- );
- $this->render('columns_definitions/column_definitions_form', $templateData);
- }
- public function partitioning(): void
- {
- if (isset($_POST['save_partitioning'])) {
- $this->dbi->selectDb($this->db);
- $this->updatePartitioning();
- $this->index();
- return;
- }
- $pageSettings = new PageSettings('TableStructure');
- $this->response->addHTML($pageSettings->getErrorHTML());
- $this->response->addHTML($pageSettings->getHTML());
- $this->addScriptFiles(['table/structure.js', 'indexes.js']);
- $partitionDetails = null;
- if (! isset($_POST['partition_by'])) {
- $partitionDetails = $this->extractPartitionDetails();
- }
- $storageEngines = StorageEngine::getArray();
- $partitionDetails = TablePartitionDefinition::getDetails($partitionDetails);
- $this->render('table/structure/partition_definition_form', [
- 'db' => $this->db,
- 'table' => $this->table,
- 'partition_details' => $partitionDetails,
- 'storage_engines' => $storageEngines,
- ]);
- }
- /**
- * Extracts partition details from CREATE TABLE statement
- *
- * @return array[]|null array of partition details
- */
- private function extractPartitionDetails(): ?array
- {
- $createTable = (new Table($this->table, $this->db))->showCreate();
- if (! $createTable) {
- return null;
- }
- $parser = new Parser($createTable);
- /**
- * @var CreateStatement $stmt
- */
- $stmt = $parser->statements[0];
- $partitionDetails = [];
- $partitionDetails['partition_by'] = '';
- $partitionDetails['partition_expr'] = '';
- $partitionDetails['partition_count'] = '';
- if (! empty($stmt->partitionBy)) {
- $openPos = strpos($stmt->partitionBy, '(');
- $closePos = strrpos($stmt->partitionBy, ')');
- $partitionDetails['partition_by']
- = trim(substr($stmt->partitionBy, 0, $openPos));
- $partitionDetails['partition_expr']
- = trim(substr($stmt->partitionBy, $openPos + 1, $closePos - ($openPos + 1)));
- if (isset($stmt->partitionsNum)) {
- $count = $stmt->partitionsNum;
- } else {
- $count = count($stmt->partitions);
- }
- $partitionDetails['partition_count'] = $count;
- }
- $partitionDetails['subpartition_by'] = '';
- $partitionDetails['subpartition_expr'] = '';
- $partitionDetails['subpartition_count'] = '';
- if (! empty($stmt->subpartitionBy)) {
- $openPos = strpos($stmt->subpartitionBy, '(');
- $closePos = strrpos($stmt->subpartitionBy, ')');
- $partitionDetails['subpartition_by']
- = trim(substr($stmt->subpartitionBy, 0, $openPos));
- $partitionDetails['subpartition_expr']
- = trim(substr($stmt->subpartitionBy, $openPos + 1, $closePos - ($openPos + 1)));
- if (isset($stmt->subpartitionsNum)) {
- $count = $stmt->subpartitionsNum;
- } else {
- $count = count($stmt->partitions[0]->subpartitions);
- }
- $partitionDetails['subpartition_count'] = $count;
- }
- // Only LIST and RANGE type parameters allow subpartitioning
- $partitionDetails['can_have_subpartitions']
- = $partitionDetails['partition_count'] > 1
- && ($partitionDetails['partition_by'] === 'RANGE'
- || $partitionDetails['partition_by'] === 'RANGE COLUMNS'
- || $partitionDetails['partition_by'] === 'LIST'
- || $partitionDetails['partition_by'] === 'LIST COLUMNS');
- // Values are specified only for LIST and RANGE type partitions
- $partitionDetails['value_enabled'] = isset($partitionDetails['partition_by'])
- && ($partitionDetails['partition_by'] === 'RANGE'
- || $partitionDetails['partition_by'] === 'RANGE COLUMNS'
- || $partitionDetails['partition_by'] === 'LIST'
- || $partitionDetails['partition_by'] === 'LIST COLUMNS');
- $partitionDetails['partitions'] = [];
- for ($i = 0, $iMax = (int) $partitionDetails['partition_count']; $i < $iMax; $i++) {
- if (! isset($stmt->partitions[$i])) {
- $partitionDetails['partitions'][$i] = [
- 'name' => 'p' . $i,
- 'value_type' => '',
- 'value' => '',
- 'engine' => '',
- 'comment' => '',
- 'data_directory' => '',
- 'index_directory' => '',
- 'max_rows' => '',
- 'min_rows' => '',
- 'tablespace' => '',
- 'node_group' => '',
- ];
- } else {
- $p = $stmt->partitions[$i];
- $type = $p->type;
- $expr = trim((string) $p->expr, '()');
- if ($expr === 'MAXVALUE') {
- $type .= ' MAXVALUE';
- $expr = '';
- }
- $partitionDetails['partitions'][$i] = [
- 'name' => $p->name,
- 'value_type' => $type,
- 'value' => $expr,
- 'engine' => $p->options->has('ENGINE', true),
- 'comment' => trim((string) $p->options->has('COMMENT', true), "'"),
- 'data_directory' => trim((string) $p->options->has('DATA DIRECTORY', true), "'"),
- 'index_directory' => trim((string) $p->options->has('INDEX_DIRECTORY', true), "'"),
- 'max_rows' => $p->options->has('MAX_ROWS', true),
- 'min_rows' => $p->options->has('MIN_ROWS', true),
- 'tablespace' => $p->options->has('TABLESPACE', true),
- 'node_group' => $p->options->has('NODEGROUP', true),
- ];
- }
- $partition =& $partitionDetails['partitions'][$i];
- $partition['prefix'] = 'partitions[' . $i . ']';
- if ($partitionDetails['subpartition_count'] <= 1) {
- continue;
- }
- $partition['subpartition_count'] = $partitionDetails['subpartition_count'];
- $partition['subpartitions'] = [];
- for ($j = 0, $jMax = (int) $partitionDetails['subpartition_count']; $j < $jMax; $j++) {
- if (! isset($stmt->partitions[$i]->subpartitions[$j])) {
- $partition['subpartitions'][$j] = [
- 'name' => $partition['name'] . '_s' . $j,
- 'engine' => '',
- 'comment' => '',
- 'data_directory' => '',
- 'index_directory' => '',
- 'max_rows' => '',
- 'min_rows' => '',
- 'tablespace' => '',
- 'node_group' => '',
- ];
- } else {
- $sp = $stmt->partitions[$i]->subpartitions[$j];
- $partition['subpartitions'][$j] = [
- 'name' => $sp->name,
- 'engine' => $sp->options->has('ENGINE', true),
- 'comment' => trim((string) $sp->options->has('COMMENT', true), "'"),
- 'data_directory' => trim((string) $sp->options->has('DATA DIRECTORY', true), "'"),
- 'index_directory' => trim((string) $sp->options->has('INDEX_DIRECTORY', true), "'"),
- 'max_rows' => $sp->options->has('MAX_ROWS', true),
- 'min_rows' => $sp->options->has('MIN_ROWS', true),
- 'tablespace' => $sp->options->has('TABLESPACE', true),
- 'node_group' => $sp->options->has('NODEGROUP', true),
- ];
- }
- $subpartition =& $partition['subpartitions'][$j];
- $subpartition['prefix'] = 'partitions[' . $i . ']'
- . '[subpartitions][' . $j . ']';
- }
- }
- return $partitionDetails;
- }
- private function updatePartitioning(): void
- {
- $sql_query = 'ALTER TABLE ' . Util::backquote($this->table) . ' '
- . $this->createAddField->getPartitionsDefinition();
- // Execute alter query
- $result = $this->dbi->tryQuery($sql_query);
- if ($result === false) {
- $this->response->setRequestStatus(false);
- $this->response->addJSON(
- 'message',
- Message::rawError(
- __('Query error') . ':<br>' . $this->dbi->getError()
- )
- );
- return;
- }
- $message = Message::success(
- __('Table %1$s has been altered successfully.')
- );
- $message->addParam($this->table);
- $this->response->addHTML(
- Generator::getMessage($message, $sql_query, 'success')
- );
- }
- /**
- * Function to display table browse for selected columns
- *
- * @param string $goto goto page url
- * @param string $themeImagePath URI of the pma theme image
- *
- * @return void
- */
- protected function displayTableBrowseForSelectedColumns($goto, $themeImagePath)
- {
- $GLOBALS['active_page'] = Url::getFromRoute('/sql');
- $fields = [];
- foreach ($_POST['selected_fld'] as $sval) {
- $fields[] = Util::backquote($sval);
- }
- $sql_query = sprintf(
- 'SELECT %s FROM %s.%s',
- implode(', ', $fields),
- Util::backquote($this->db),
- Util::backquote($this->table)
- );
- // Parse and analyze the query
- $db = &$this->db;
- [
- $analyzed_sql_results,
- $db,
- ] = ParseAnalyze::sqlQuery($sql_query, $db);
- $sql = new Sql(
- $this->dbi,
- $this->relation,
- $this->relationCleanup,
- new Operations($this->dbi, $this->relation),
- $this->transformations,
- $this->template
- );
- $this->response->addHTML(
- $sql->executeQueryAndGetQueryResponse(
- $analyzed_sql_results ?? '',
- false, // is_gotofile
- $this->db, // db
- $this->table, // table
- null, // find_real_end
- null, // sql_query_for_bookmark
- null, // extra_data
- null, // message_to_show
- null, // sql_data
- $goto, // goto
- $themeImagePath,
- null, // disp_query
- null, // disp_message
- $sql_query, // sql_query
- null // complete_query
- )
- );
- }
- /**
- * Update the table's structure based on $_REQUEST
- *
- * @return bool true if error occurred
- */
- protected function updateColumns()
- {
- $err_url = Url::getFromRoute('/table/structure', [
- 'db' => $this->db,
- 'table' => $this->table,
- ]);
- $regenerate = false;
- $field_cnt = count($_POST['field_name'] ?? []);
- $changes = [];
- $adjust_privileges = [];
- $columns_with_index = $this->dbi
- ->getTable($this->db, $this->table)
- ->getColumnsWithIndex(
- Index::PRIMARY | Index::UNIQUE
- );
- for ($i = 0; $i < $field_cnt; $i++) {
- if (! $this->columnNeedsAlterTable($i)) {
- continue;
- }
- $changes[] = 'CHANGE ' . Table::generateAlter(
- Util::getValueByKey($_POST, "field_orig.${i}", ''),
- $_POST['field_name'][$i],
- $_POST['field_type'][$i],
- $_POST['field_length'][$i],
- $_POST['field_attribute'][$i],
- Util::getValueByKey($_POST, "field_collation.${i}", ''),
- Util::getValueByKey($_POST, "field_null.${i}", 'NO'),
- $_POST['field_default_type'][$i],
- $_POST['field_default_value'][$i],
- Util::getValueByKey($_POST, "field_extra.${i}", false),
- Util::getValueByKey($_POST, "field_comments.${i}", ''),
- Util::getValueByKey($_POST, "field_virtuality.${i}", ''),
- Util::getValueByKey($_POST, "field_expression.${i}", ''),
- Util::getValueByKey($_POST, "field_move_to.${i}", ''),
- $columns_with_index
- );
- // find the remembered sort expression
- $sorted_col = $this->tableObj->getUiProp(
- Table::PROP_SORTED_COLUMN
- );
- // if the old column name is part of the remembered sort expression
- if (mb_strpos(
- (string) $sorted_col,
- Util::backquote($_POST['field_orig'][$i])
- ) !== false) {
- // delete the whole remembered sort expression
- $this->tableObj->removeUiProp(Table::PROP_SORTED_COLUMN);
- }
- if (! isset($_POST['field_adjust_privileges'][$i])
- || empty($_POST['field_adjust_privileges'][$i])
- || $_POST['field_orig'][$i] == $_POST['field_name'][$i]
- ) {
- continue;
- }
- $adjust_privileges[$_POST['field_orig'][$i]]
- = $_POST['field_name'][$i];
- }
- if (count($changes) > 0 || isset($_POST['preview_sql'])) {
- // Builds the primary keys statements and updates the table
- $key_query = '';
- /**
- * this is a little bit more complex
- *
- * @todo if someone selects A_I when altering a column we need to check:
- * - no other column with A_I
- * - the column has an index, if not create one
- */
- // To allow replication, we first select the db to use
- // and then run queries on this db.
- if (! $this->dbi->selectDb($this->db)) {
- Generator::mysqlDie(
- $this->dbi->getError(),
- 'USE ' . Util::backquote($this->db) . ';',
- false,
- $err_url
- );
- }
- $sql_query = 'ALTER TABLE ' . Util::backquote($this->table) . ' ';
- $sql_query .= implode(', ', $changes) . $key_query;
- if (isset($_POST['online_transaction'])) {
- $sql_query .= ', ALGORITHM=INPLACE, LOCK=NONE';
- }
- $sql_query .= ';';
- // If there is a request for SQL previewing.
- if (isset($_POST['preview_sql'])) {
- Core::previewSQL(count($changes) > 0 ? $sql_query : '');
- exit;
- }
- $columns_with_index = $this->dbi
- ->getTable($this->db, $this->table)
- ->getColumnsWithIndex(
- Index::PRIMARY | Index::UNIQUE | Index::INDEX
- | Index::SPATIAL | Index::FULLTEXT
- );
- $changedToBlob = [];
- // While changing the Column Collation
- // First change to BLOB
- for ($i = 0; $i < $field_cnt; $i++) {
- if (isset($_POST['field_collation'][$i], $_POST['field_collation_orig'][$i])
- && $_POST['field_collation'][$i] !== $_POST['field_collation_orig'][$i]
- && ! in_array($_POST['field_orig'][$i], $columns_with_index)
- ) {
- $secondary_query = 'ALTER TABLE ' . Util::backquote(
- $this->table
- )
- . ' CHANGE ' . Util::backquote(
- $_POST['field_orig'][$i]
- )
- . ' ' . Util::backquote($_POST['field_orig'][$i])
- . ' BLOB';
- if (isset($_POST['field_virtuality'][$i], $_POST['field_expression'][$i])) {
- if ($_POST['field_virtuality'][$i]) {
- $secondary_query .= ' AS (' . $_POST['field_expression'][$i] . ') '
- . $_POST['field_virtuality'][$i];
- }
- }
- $secondary_query .= ';';
- $this->dbi->query($secondary_query);
- $changedToBlob[$i] = true;
- } else {
- $changedToBlob[$i] = false;
- }
- }
- // Then make the requested changes
- $result = $this->dbi->tryQuery($sql_query);
- if ($result !== false) {
- $changed_privileges = $this->adjustColumnPrivileges(
- $adjust_privileges
- );
- if ($changed_privileges) {
- $message = Message::success(
- __(
- 'Table %1$s has been altered successfully. Privileges ' .
- 'have been adjusted.'
- )
- );
- } else {
- $message = Message::success(
- __('Table %1$s has been altered successfully.')
- );
- }
- $message->addParam($this->table);
- $this->response->addHTML(
- Generator::getMessage($message, $sql_query, 'success')
- );
- } else {
- // An error happened while inserting/updating a table definition
- // Save the Original Error
- $orig_error = $this->dbi->getError();
- $changes_revert = [];
- // Change back to Original Collation and data type
- for ($i = 0; $i < $field_cnt; $i++) {
- if (! $changedToBlob[$i]) {
- continue;
- }
- $changes_revert[] = 'CHANGE ' . Table::generateAlter(
- Util::getValueByKey($_POST, "field_orig.${i}", ''),
- $_POST['field_name'][$i],
- $_POST['field_type_orig'][$i],
- $_POST['field_length_orig'][$i],
- $_POST['field_attribute_orig'][$i],
- Util::getValueByKey($_POST, "field_collation_orig.${i}", ''),
- Util::getValueByKey($_POST, "field_null_orig.${i}", 'NO'),
- $_POST['field_default_type_orig'][$i],
- $_POST['field_default_value_orig'][$i],
- Util::getValueByKey($_POST, "field_extra_orig.${i}", false),
- Util::getValueByKey($_POST, "field_comments_orig.${i}", ''),
- Util::getValueByKey($_POST, "field_virtuality_orig.${i}", ''),
- Util::getValueByKey($_POST, "field_expression_orig.${i}", ''),
- Util::getValueByKey($_POST, "field_move_to_orig.${i}", '')
- );
- }
- $revert_query = 'ALTER TABLE ' . Util::backquote($this->table)
- . ' ';
- $revert_query .= implode(', ', $changes_revert) . '';
- $revert_query .= ';';
- // Column reverted back to original
- $this->dbi->query($revert_query);
- $this->response->setRequestStatus(false);
- $this->response->addJSON(
- 'message',
- Message::rawError(
- __('Query error') . ':<br>' . $orig_error
- )
- );
- $regenerate = true;
- }
- }
- // update field names in relation
- if (isset($_POST['field_orig']) && is_array($_POST['field_orig'])) {
- foreach ($_POST['field_orig'] as $fieldindex => $fieldcontent) {
- if ($_POST['field_name'][$fieldindex] == $fieldcontent) {
- continue;
- }
- $this->relation->renameField(
- $this->db,
- $this->table,
- $fieldcontent,
- $_POST['field_name'][$fieldindex]
- );
- }
- }
- // update mime types
- if (isset($_POST['field_mimetype'])
- && is_array($_POST['field_mimetype'])
- && $GLOBALS['cfg']['BrowseMIME']
- ) {
- foreach ($_POST['field_mimetype'] as $fieldindex => $mimetype) {
- if (! isset($_POST['field_name'][$fieldindex])
- || strlen($_POST['field_name'][$fieldindex]) <= 0
- ) {
- continue;
- }
- $this->transformations->setMime(
- $this->db,
- $this->table,
- $_POST['field_name'][$fieldindex],
- $mimetype,
- $_POST['field_transformation'][$fieldindex],
- $_POST['field_transformation_options'][$fieldindex],
- $_POST['field_input_transformation'][$fieldindex],
- $_POST['field_input_transformation_options'][$fieldindex]
- );
- }
- }
- return $regenerate;
- }
- /**
- * Adjusts the Privileges for all the columns whose names have changed
- *
- * @param array $adjust_privileges assoc array of old col names mapped to new
- * cols
- *
- * @return bool boolean whether at least one column privileges
- * adjusted
- */
- protected function adjustColumnPrivileges(array $adjust_privileges)
- {
- $changed = false;
- if (Util::getValueByKey($GLOBALS, 'col_priv', false)
- && Util::getValueByKey($GLOBALS, 'is_reload_priv', false)
- ) {
- $this->dbi->selectDb('mysql');
- // For Column specific privileges
- foreach ($adjust_privileges as $oldCol => $newCol) {
- $this->dbi->query(
- sprintf(
- 'UPDATE %s SET Column_name = "%s"
- WHERE Db = "%s"
- AND Table_name = "%s"
- AND Column_name = "%s";',
- Util::backquote('columns_priv'),
- $newCol,
- $this->db,
- $this->table,
- $oldCol
- )
- );
- // i.e. if atleast one column privileges adjusted
- $changed = true;
- }
- if ($changed) {
- // Finally FLUSH the new privileges
- $this->dbi->query('FLUSH PRIVILEGES;');
- }
- }
- return $changed;
- }
- /**
- * Verifies if some elements of a column have changed
- *
- * @param int $i column index in the request
- *
- * @return bool true if we need to generate ALTER TABLE
- */
- protected function columnNeedsAlterTable($i)
- {
- // these two fields are checkboxes so might not be part of the
- // request; therefore we define them to avoid notices below
- if (! isset($_POST['field_null'][$i])) {
- $_POST['field_null'][$i] = 'NO';
- }
- if (! isset($_POST['field_extra'][$i])) {
- $_POST['field_extra'][$i] = '';
- }
- // field_name does not follow the convention (corresponds to field_orig)
- if ($_POST['field_name'][$i] != $_POST['field_orig'][$i]) {
- return true;
- }
- $fields = [
- 'field_attribute',
- 'field_collation',
- 'field_comments',
- 'field_default_value',
- 'field_default_type',
- 'field_extra',
- 'field_length',
- 'field_null',
- 'field_type',
- ];
- foreach ($fields as $field) {
- if ($_POST[$field][$i] != $_POST[$field . '_orig'][$i]) {
- return true;
- }
- }
- return ! empty($_POST['field_move_to'][$i]);
- }
- /**
- * Displays the table structure ('show table' works correct since 3.23.03)
- *
- * @param array $cfgRelation current relation parameters
- * @param array $columns_with_unique_index Columns with unique index
- * @param Index|false $primary_index primary index or false if
- * no one exists
- * @param array $fields Fields
- * @param array $columns_with_index Columns with index
- *
- * @return string
- */
- protected function displayStructure(
- array $cfgRelation,
- array $columns_with_unique_index,
- $primary_index,
- array $fields,
- array $columns_with_index,
- bool $isSystemSchema
- ) {
- global $route, $tbl_is_view, $tbl_storage_engine, $PMA_Theme;
- // prepare comments
- $comments_map = [];
- $mime_map = [];
- if ($GLOBALS['cfg']['ShowPropertyComments']) {
- $comments_map = $this->relation->getComments($this->db, $this->table);
- if ($cfgRelation['mimework'] && $GLOBALS['cfg']['BrowseMIME']) {
- $mime_map = $this->transformations->getMime($this->db, $this->table, true);
- }
- }
- $centralColumns = new CentralColumns($this->dbi);
- $central_list = $centralColumns->getFromTable(
- $this->db,
- $this->table
- );
- /**
- * Displays Space usage and row statistics
- */
- // BEGIN - Calc Table Space
- // Get valid statistics whatever is the table type
- if ($GLOBALS['cfg']['ShowStats']) {
- //get table stats in HTML format
- $tablestats = $this->getTableStats($isSystemSchema);
- //returning the response in JSON format to be used by Ajax
- $this->response->addJSON('tableStat', $tablestats);
- }
- // END - Calc Table Space
- $hideStructureActions = false;
- if ($GLOBALS['cfg']['HideStructureActions'] === true) {
- $hideStructureActions = true;
- }
- // logic removed from Template
- $rownum = 0;
- $columns_list = [];
- $attributes = [];
- $displayed_fields = [];
- $row_comments = [];
- $extracted_columnspecs = [];
- $collations = [];
- foreach ($fields as &$field) {
- ++$rownum;
- $columns_list[] = $field['Field'];
- $extracted_columnspecs[$rownum] = Util::extractColumnSpec($field['Type']);
- $attributes[$rownum] = $extracted_columnspecs[$rownum]['attribute'];
- if (strpos($field['Extra'], 'on update CURRENT_TIMESTAMP') !== false) {
- $attributes[$rownum] = 'on update CURRENT_TIMESTAMP';
- }
- $displayed_fields[$rownum] = new stdClass();
- $displayed_fields[$rownum]->text = $field['Field'];
- $displayed_fields[$rownum]->icon = '';
- $row_comments[$rownum] = '';
- if (isset($comments_map[$field['Field']])) {
- $displayed_fields[$rownum]->comment = $comments_map[$field['Field']];
- $row_comments[$rownum] = $comments_map[$field['Field']];
- }
- if ($primary_index && $primary_index->hasColumn($field['Field'])) {
- $displayed_fields[$rownum]->icon .=
- Generator::getImage('b_primary', __('Primary'));
- }
- if (in_array($field['Field'], $columns_with_index)) {
- $displayed_fields[$rownum]->icon .=
- Generator::getImage('bd_primary', __('Index'));
- }
- $collation = Charsets::findCollationByName(
- $this->dbi,
- $GLOBALS['cfg']['Server']['DisableIS'],
- $field['Collation'] ?? ''
- );
- if ($collation === null) {
- continue;
- }
- $collations[$collation->getName()] = [
- 'name' => $collation->getName(),
- 'description' => $collation->getDescription(),
- ];
- }
- $engine = $this->tableObj->getStorageEngine();
- return $this->template->render('table/structure/display_structure', [
- 'collations' => $collations,
- 'is_foreign_key_supported' => Util::isForeignKeySupported($engine),
- 'indexes' => Index::getFromTable($this->table, $this->db),
- 'indexes_duplicates' => Index::findDuplicates($this->table, $this->db),
- 'cfg_relation' => $this->relation->getRelationsParam(),
- 'hide_structure_actions' => $hideStructureActions,
- 'db' => $this->db,
- 'table' => $this->table,
- 'db_is_system_schema' => $isSystemSchema,
- 'tbl_is_view' => $tbl_is_view,
- 'mime_map' => $mime_map,
- 'tbl_storage_engine' => $tbl_storage_engine,
- 'primary' => $primary_index,
- 'columns_with_unique_index' => $columns_with_unique_index,
- 'columns_list' => $columns_list,
- 'table_stats' => $tablestats ?? null,
- 'fields' => $fields,
- 'extracted_columnspecs' => $extracted_columnspecs,
- 'columns_with_index' => $columns_with_index,
- 'central_list' => $central_list,
- 'comments_map' => $comments_map,
- 'browse_mime' => $GLOBALS['cfg']['BrowseMIME'],
- 'show_column_comments' => $GLOBALS['cfg']['ShowColumnComments'],
- 'show_stats' => $GLOBALS['cfg']['ShowStats'],
- 'relation_commwork' => $GLOBALS['cfgRelation']['commwork'],
- 'relation_mimework' => $GLOBALS['cfgRelation']['mimework'],
- 'central_columns_work' => $GLOBALS['cfgRelation']['centralcolumnswork'],
- 'mysql_int_version' => $this->dbi->getVersion(),
- 'is_mariadb' => $this->dbi->isMariaDB(),
- 'theme_image_path' => $PMA_Theme->getImgPath(),
- 'text_dir' => $GLOBALS['text_dir'],
- 'is_active' => Tracker::isActive(),
- 'have_partitioning' => Partition::havePartitioning(),
- 'partitions' => Partition::getPartitions($this->db, $this->table),
- 'partition_names' => Partition::getPartitionNames($this->db, $this->table),
- 'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'],
- 'attributes' => $attributes,
- 'displayed_fields' => $displayed_fields,
- 'row_comments' => $row_comments,
- 'route' => $route,
- ]);
- }
- /**
- * Get HTML snippet for display table statistics
- *
- * @return string
- */
- protected function getTableStats(bool $isSystemSchema)
- {
- global $showtable, $tbl_is_view;
- global $tbl_storage_engine, $table_info_num_rows, $tbl_collation;
- if (empty($showtable)) {
- $showtable = $this->dbi->getTable(
- $this->db,
- $this->table
- )->getStatusInfo(null, true);
- }
- if (is_string($showtable)) {
- $showtable = [];
- }
- if (empty($showtable['Data_length'])) {
- $showtable['Data_length'] = 0;
- }
- if (empty($showtable['Index_length'])) {
- $showtable['Index_length'] = 0;
- }
- $is_innodb = (isset($showtable['Type'])
- && $showtable['Type'] === 'InnoDB');
- $mergetable = $this->tableObj->isMerge();
- // this is to display for example 261.2 MiB instead of 268k KiB
- $max_digits = 3;
- $decimals = 1;
- [$data_size, $data_unit] = Util::formatByteDown(
- $showtable['Data_length'],
- $max_digits,
- $decimals
- );
- if ($mergetable === false) {
- [$index_size, $index_unit] = Util::formatByteDown(
- $showtable['Index_length'],
- $max_digits,
- $decimals
- );
- }
- if (isset($showtable['Data_free'])) {
- [$free_size, $free_unit] = Util::formatByteDown(
- $showtable['Data_free'],
- $max_digits,
- $decimals
- );
- [$effect_size, $effect_unit] = Util::formatByteDown(
- $showtable['Data_length']
- + $showtable['Index_length']
- - $showtable['Data_free'],
- $max_digits,
- $decimals
- );
- } else {
- [$effect_size, $effect_unit] = Util::formatByteDown(
- $showtable['Data_length']
- + $showtable['Index_length'],
- $max_digits,
- $decimals
- );
- }
- [$tot_size, $tot_unit] = Util::formatByteDown(
- $showtable['Data_length'] + $showtable['Index_length'],
- $max_digits,
- $decimals
- );
- if ($table_info_num_rows > 0) {
- [$avg_size, $avg_unit] = Util::formatByteDown(
- ($showtable['Data_length']
- + $showtable['Index_length'])
- / $showtable['Rows'],
- 6,
- 1
- );
- } else {
- $avg_size = $avg_unit = '';
- }
- /** @var Innodb $innodbEnginePlugin */
- $innodbEnginePlugin = StorageEngine::getEngine('Innodb');
- $innodb_file_per_table = $innodbEnginePlugin->supportsFilePerTable();
- $engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine();
- $tableCollation = [];
- $collation = Charsets::findCollationByName(
- $this->dbi,
- $GLOBALS['cfg']['Server']['DisableIS'],
- $tbl_collation
- );
- if ($collation !== null) {
- $tableCollation = [
- 'name' => $collation->getName(),
- 'description' => $collation->getDescription(),
- ];
- }
- return $this->template->render('table/structure/display_table_stats', [
- 'db' => $GLOBALS['db'],
- 'table' => $GLOBALS['table'],
- 'is_foreign_key_supported' => Util::isForeignKeySupported($engine),
- 'cfg_relation' => $this->relation->getRelationsParam(),
- 'showtable' => $showtable,
- 'table_info_num_rows' => $table_info_num_rows,
- 'tbl_is_view' => $tbl_is_view,
- 'db_is_system_schema' => $isSystemSchema,
- 'tbl_storage_engine' => $tbl_storage_engine,
- 'table_collation' => $tableCollation,
- 'is_innodb' => $is_innodb,
- 'mergetable' => $mergetable,
- 'avg_size' => $avg_size ?? null,
- 'avg_unit' => $avg_unit ?? null,
- 'data_size' => $data_size,
- 'data_unit' => $data_unit,
- 'index_size' => $index_size ?? null,
- 'index_unit' => $index_unit ?? null,
- 'innodb_file_per_table' => $innodb_file_per_table,
- 'free_size' => $free_size ?? null,
- 'free_unit' => $free_unit ?? null,
- 'effect_size' => $effect_size,
- 'effect_unit' => $effect_unit,
- 'tot_size' => $tot_size,
- 'tot_unit' => $tot_unit,
- ]);
- }
- /**
- * Gets table primary key
- *
- * @return string
- */
- protected function getKeyForTablePrimary()
- {
- $this->dbi->selectDb($this->db);
- $result = $this->dbi->query(
- 'SHOW KEYS FROM ' . Util::backquote($this->table) . ';'
- );
- $primary = '';
- while ($row = $this->dbi->fetchAssoc($result)) {
- // Backups the list of primary keys
- if (! is_array($row) || $row['Key_name'] !== 'PRIMARY') {
- continue;
- }
- $primary .= $row['Column_name'] . ', ';
- }
- $this->dbi->freeResult($result);
- return $primary;
- }
- /**
- * Handles MySQL reserved words columns check.
- */
- public function reservedWordCheck(): void
- {
- if ($GLOBALS['cfg']['ReservedWordDisableWarning'] !== false) {
- $this->response->setRequestStatus(false);
- return;
- }
- $columns_names = $_POST['field_name'];
- $reserved_keywords_names = [];
- foreach ($columns_names as $column) {
- if (! Context::isKeyword(trim($column), true)) {
- continue;
- }
- $reserved_keywords_names[] = trim($column);
- }
- if (Context::isKeyword(trim($this->table), true)) {
- $reserved_keywords_names[] = trim($this->table);
- }
- if (count($reserved_keywords_names) === 0) {
- $this->response->setRequestStatus(false);
- }
- $this->response->addJSON(
- 'message',
- sprintf(
- _ngettext(
- 'The name \'%s\' is a MySQL reserved keyword.',
- 'The names \'%s\' are MySQL reserved keywords.',
- count($reserved_keywords_names)
- ),
- implode(',', $reserved_keywords_names)
- )
- );
- }
- }
|