You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
918 lines
32 KiB
918 lines
32 KiB
<?php |
|
|
|
declare(strict_types=1); |
|
|
|
namespace PhpMyAdmin\Controllers\Database; |
|
|
|
use PhpMyAdmin\Charsets; |
|
use PhpMyAdmin\CheckUserPrivileges; |
|
use PhpMyAdmin\Config\PageSettings; |
|
use PhpMyAdmin\ConfigStorage\Relation; |
|
use PhpMyAdmin\ConfigStorage\RelationCleanup; |
|
use PhpMyAdmin\DatabaseInterface; |
|
use PhpMyAdmin\FlashMessages; |
|
use PhpMyAdmin\Html\Generator; |
|
use PhpMyAdmin\Operations; |
|
use PhpMyAdmin\RecentFavoriteTable; |
|
use PhpMyAdmin\Replication; |
|
use PhpMyAdmin\ReplicationInfo; |
|
use PhpMyAdmin\ResponseRenderer; |
|
use PhpMyAdmin\Sanitize; |
|
use PhpMyAdmin\StorageEngine; |
|
use PhpMyAdmin\Template; |
|
use PhpMyAdmin\Tracker; |
|
use PhpMyAdmin\Url; |
|
use PhpMyAdmin\Util; |
|
|
|
use function __; |
|
use function array_search; |
|
use function ceil; |
|
use function count; |
|
use function htmlspecialchars; |
|
use function implode; |
|
use function in_array; |
|
use function is_string; |
|
use function max; |
|
use function mb_substr; |
|
use function md5; |
|
use function preg_match; |
|
use function preg_quote; |
|
use function sprintf; |
|
use function str_replace; |
|
use function strlen; |
|
use function strtotime; |
|
use function urlencode; |
|
|
|
/** |
|
* Handles database structure logic |
|
*/ |
|
class StructureController extends AbstractController |
|
{ |
|
/** @var int Number of tables */ |
|
protected $numTables; |
|
|
|
/** @var int Current position in the list */ |
|
protected $position; |
|
|
|
/** @var bool DB is information_schema */ |
|
protected $dbIsSystemSchema; |
|
|
|
/** @var int Number of tables */ |
|
protected $totalNumTables; |
|
|
|
/** @var array Tables in the database */ |
|
protected $tables; |
|
|
|
/** @var bool whether stats show or not */ |
|
protected $isShowStats; |
|
|
|
/** @var Relation */ |
|
private $relation; |
|
|
|
/** @var Replication */ |
|
private $replication; |
|
|
|
/** @var RelationCleanup */ |
|
private $relationCleanup; |
|
|
|
/** @var Operations */ |
|
private $operations; |
|
|
|
/** @var ReplicationInfo */ |
|
private $replicationInfo; |
|
|
|
/** @var DatabaseInterface */ |
|
private $dbi; |
|
|
|
/** @var FlashMessages */ |
|
private $flash; |
|
|
|
public function __construct( |
|
ResponseRenderer $response, |
|
Template $template, |
|
string $db, |
|
Relation $relation, |
|
Replication $replication, |
|
RelationCleanup $relationCleanup, |
|
Operations $operations, |
|
DatabaseInterface $dbi, |
|
FlashMessages $flash |
|
) { |
|
parent::__construct($response, $template, $db); |
|
$this->relation = $relation; |
|
$this->replication = $replication; |
|
$this->relationCleanup = $relationCleanup; |
|
$this->operations = $operations; |
|
$this->dbi = $dbi; |
|
$this->flash = $flash; |
|
|
|
$this->replicationInfo = new ReplicationInfo($this->dbi); |
|
} |
|
|
|
/** |
|
* Retrieves database information for further use |
|
* |
|
* @param string $subPart Page part name |
|
*/ |
|
private function getDatabaseInfo(string $subPart): void |
|
{ |
|
[ |
|
$tables, |
|
$numTables, |
|
$totalNumTables,, |
|
$isShowStats, |
|
$dbIsSystemSchema,,, |
|
$position, |
|
] = Util::getDbInfo($this->db, $subPart); |
|
|
|
$this->tables = $tables; |
|
$this->numTables = $numTables; |
|
$this->position = $position; |
|
$this->dbIsSystemSchema = $dbIsSystemSchema; |
|
$this->totalNumTables = $totalNumTables; |
|
$this->isShowStats = $isShowStats; |
|
} |
|
|
|
public function __invoke(): void |
|
{ |
|
global $cfg, $db, $errorUrl; |
|
|
|
$parameters = [ |
|
'sort' => $_REQUEST['sort'] ?? null, |
|
'sort_order' => $_REQUEST['sort_order'] ?? null, |
|
]; |
|
|
|
Util::checkParameters(['db']); |
|
|
|
$errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); |
|
$errorUrl .= Url::getCommon(['db' => $db], '&'); |
|
|
|
if (! $this->hasDatabase()) { |
|
return; |
|
} |
|
|
|
$this->addScriptFiles(['database/structure.js', 'table/change.js']); |
|
|
|
// Gets the database structure |
|
$this->getDatabaseInfo('_structure'); |
|
|
|
// Checks if there are any tables to be shown on current page. |
|
// If there are no tables, the user is redirected to the last page |
|
// having any. |
|
if ($this->totalNumTables > 0 && $this->position > $this->totalNumTables) { |
|
$this->redirect('/database/structure', [ |
|
'db' => $this->db, |
|
'pos' => max(0, $this->totalNumTables - $cfg['MaxTableList']), |
|
'reload' => 1, |
|
]); |
|
} |
|
|
|
$this->replicationInfo->load($_POST['primary_connection'] ?? null); |
|
$replicaInfo = $this->replicationInfo->getReplicaInfo(); |
|
|
|
$pageSettings = new PageSettings('DbStructure'); |
|
$this->response->addHTML($pageSettings->getErrorHTML()); |
|
$this->response->addHTML($pageSettings->getHTML()); |
|
|
|
if ($this->numTables > 0) { |
|
$urlParams = [ |
|
'pos' => $this->position, |
|
'db' => $this->db, |
|
]; |
|
if (isset($parameters['sort'])) { |
|
$urlParams['sort'] = $parameters['sort']; |
|
} |
|
|
|
if (isset($parameters['sort_order'])) { |
|
$urlParams['sort_order'] = $parameters['sort_order']; |
|
} |
|
|
|
$listNavigator = Generator::getListNavigator( |
|
$this->totalNumTables, |
|
$this->position, |
|
$urlParams, |
|
Url::getFromRoute('/database/structure'), |
|
'frame_content', |
|
$cfg['MaxTableList'] |
|
); |
|
|
|
$tableList = $this->displayTableList($replicaInfo); |
|
} |
|
|
|
$createTable = ''; |
|
if (empty($this->dbIsSystemSchema)) { |
|
$checkUserPrivileges = new CheckUserPrivileges($this->dbi); |
|
$checkUserPrivileges->getPrivileges(); |
|
|
|
$createTable = $this->template->render('database/create_table', ['db' => $this->db]); |
|
} |
|
|
|
$this->render('database/structure/index', [ |
|
'database' => $this->db, |
|
'has_tables' => $this->numTables > 0, |
|
'list_navigator_html' => $listNavigator ?? '', |
|
'table_list_html' => $tableList ?? '', |
|
'is_system_schema' => ! empty($this->dbIsSystemSchema), |
|
'create_table_html' => $createTable, |
|
]); |
|
} |
|
|
|
/** |
|
* @param array $replicaInfo |
|
*/ |
|
protected function displayTableList($replicaInfo): string |
|
{ |
|
$html = ''; |
|
|
|
// filtering |
|
$html .= $this->template->render('filter', ['filter_value' => '']); |
|
|
|
$i = $sumEntries = 0; |
|
$overheadCheck = false; |
|
$createTimeAll = ''; |
|
$updateTimeAll = ''; |
|
$checkTimeAll = ''; |
|
$numColumns = $GLOBALS['cfg']['PropertiesNumColumns'] > 1 |
|
? ceil($this->numTables / $GLOBALS['cfg']['PropertiesNumColumns']) + 1 |
|
: 0; |
|
$rowCount = 0; |
|
$sumSize = 0; |
|
$overheadSize = 0; |
|
|
|
$hiddenFields = []; |
|
$overallApproxRows = false; |
|
$structureTableRows = []; |
|
foreach ($this->tables as $currentTable) { |
|
// Get valid statistics whatever is the table type |
|
|
|
$dropQuery = ''; |
|
$dropMessage = ''; |
|
$overhead = ''; |
|
$inputClass = ['checkall']; |
|
|
|
// Sets parameters for links |
|
$tableUrlParams = [ |
|
'db' => $this->db, |
|
'table' => $currentTable['TABLE_NAME'], |
|
]; |
|
// do not list the previous table's size info for a view |
|
|
|
[ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit, |
|
$overheadSize, |
|
$tableIsView, |
|
$sumSize, |
|
] = $this->getStuffForEngineTypeTable($currentTable, $sumSize, $overheadSize); |
|
|
|
$curTable = $this->dbi |
|
->getTable($this->db, $currentTable['TABLE_NAME']); |
|
if (! $curTable->isMerge()) { |
|
$sumEntries += $currentTable['TABLE_ROWS']; |
|
} |
|
|
|
$collationDefinition = '---'; |
|
if (isset($currentTable['Collation'])) { |
|
$tableCollation = Charsets::findCollationByName( |
|
$this->dbi, |
|
$GLOBALS['cfg']['Server']['DisableIS'], |
|
$currentTable['Collation'] |
|
); |
|
if ($tableCollation !== null) { |
|
$collationDefinition = $this->template->render('database/structure/collation_definition', [ |
|
'valueTitle' => $tableCollation->getDescription(), |
|
'value' => $tableCollation->getName(), |
|
]); |
|
} |
|
} |
|
|
|
if ($this->isShowStats) { |
|
$overhead = '-'; |
|
if ($formattedOverhead != '') { |
|
$overhead = $this->template->render('database/structure/overhead', [ |
|
'table_url_params' => $tableUrlParams, |
|
'formatted_overhead' => $formattedOverhead, |
|
'overhead_unit' => $overheadUnit, |
|
]); |
|
$overheadCheck = true; |
|
$inputClass[] = 'tbl-overhead'; |
|
} |
|
} |
|
|
|
if ($GLOBALS['cfg']['ShowDbStructureCharset']) { |
|
$charset = ''; |
|
if (isset($tableCollation)) { |
|
$charset = $tableCollation->getCharset(); |
|
} |
|
} |
|
|
|
if ($GLOBALS['cfg']['ShowDbStructureCreation']) { |
|
$createTime = $currentTable['Create_time'] ?? ''; |
|
if ($createTime && (! $createTimeAll || $createTime < $createTimeAll)) { |
|
$createTimeAll = $createTime; |
|
} |
|
} |
|
|
|
if ($GLOBALS['cfg']['ShowDbStructureLastUpdate']) { |
|
$updateTime = $currentTable['Update_time'] ?? ''; |
|
if ($updateTime && (! $updateTimeAll || $updateTime < $updateTimeAll)) { |
|
$updateTimeAll = $updateTime; |
|
} |
|
} |
|
|
|
if ($GLOBALS['cfg']['ShowDbStructureLastCheck']) { |
|
$checkTime = $currentTable['Check_time'] ?? ''; |
|
if ($checkTime && (! $checkTimeAll || $checkTime < $checkTimeAll)) { |
|
$checkTimeAll = $checkTime; |
|
} |
|
} |
|
|
|
$truename = $currentTable['TABLE_NAME']; |
|
|
|
$i++; |
|
|
|
$rowCount++; |
|
if ($tableIsView) { |
|
$hiddenFields[] = '<input type="hidden" name="views[]" value="' |
|
. htmlspecialchars($currentTable['TABLE_NAME']) . '">'; |
|
} |
|
|
|
/* |
|
* Always activate links for Browse, Search and Empty, even if |
|
* the icons are greyed, because |
|
* 1. for views, we don't know the number of rows at this point |
|
* 2. for tables, another source could have populated them since the |
|
* page was generated |
|
* |
|
* I could have used the PHP ternary conditional operator but I find |
|
* the code easier to read without this operator. |
|
*/ |
|
$mayHaveRows = $currentTable['TABLE_ROWS'] > 0 || $tableIsView; |
|
|
|
if (! $this->dbIsSystemSchema) { |
|
$dropQuery = sprintf( |
|
'DROP %s %s', |
|
$tableIsView || $currentTable['ENGINE'] == null ? 'VIEW' |
|
: 'TABLE', |
|
Util::backquote( |
|
$currentTable['TABLE_NAME'] |
|
) |
|
); |
|
$dropMessage = sprintf( |
|
($tableIsView || $currentTable['ENGINE'] == null |
|
? __('View %s has been dropped.') |
|
: __('Table %s has been dropped.')), |
|
str_replace( |
|
' ', |
|
' ', |
|
htmlspecialchars($currentTable['TABLE_NAME']) |
|
) |
|
); |
|
} |
|
|
|
if ($numColumns > 0 && $this->numTables > $numColumns && ($rowCount % $numColumns) == 0) { |
|
$rowCount = 1; |
|
|
|
$html .= $this->template->render('database/structure/table_header', [ |
|
'db' => $this->db, |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
|
'replication' => $replicaInfo['status'], |
|
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'], |
|
'is_show_stats' => $this->isShowStats, |
|
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'], |
|
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'], |
|
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'], |
|
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'], |
|
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'], |
|
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'], |
|
'structure_table_rows' => $structureTableRows, |
|
]); |
|
$structureTableRows = []; |
|
} |
|
|
|
[$approxRows, $showSuperscript] = $this->isRowCountApproximated($currentTable, $tableIsView); |
|
|
|
[$do, $ignored] = $this->getReplicationStatus($replicaInfo, $truename); |
|
|
|
$structureTableRows[] = [ |
|
'table_name_hash' => md5($currentTable['TABLE_NAME']), |
|
'db_table_name_hash' => md5($this->db . '.' . $currentTable['TABLE_NAME']), |
|
'db' => $this->db, |
|
'curr' => $i, |
|
'input_class' => implode(' ', $inputClass), |
|
'table_is_view' => $tableIsView, |
|
'current_table' => $currentTable, |
|
'may_have_rows' => $mayHaveRows, |
|
'browse_table_label_title' => htmlspecialchars($currentTable['TABLE_COMMENT']), |
|
'browse_table_label_truename' => $truename, |
|
'empty_table_sql_query' => 'TRUNCATE ' . Util::backquote($currentTable['TABLE_NAME']), |
|
'empty_table_message_to_show' => urlencode( |
|
sprintf( |
|
__('Table %s has been emptied.'), |
|
htmlspecialchars( |
|
$currentTable['TABLE_NAME'] |
|
) |
|
) |
|
), |
|
'tracking_icon' => $this->getTrackingIcon($truename), |
|
'server_replica_status' => $replicaInfo['status'], |
|
'table_url_params' => $tableUrlParams, |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
|
'drop_query' => $dropQuery, |
|
'drop_message' => $dropMessage, |
|
'collation' => $collationDefinition, |
|
'formatted_size' => $formattedSize, |
|
'unit' => $unit, |
|
'overhead' => $overhead, |
|
'create_time' => isset($createTime) && $createTime |
|
? Util::localisedDate(strtotime($createTime)) : '-', |
|
'update_time' => isset($updateTime) && $updateTime |
|
? Util::localisedDate(strtotime($updateTime)) : '-', |
|
'check_time' => isset($checkTime) && $checkTime |
|
? Util::localisedDate(strtotime($checkTime)) : '-', |
|
'charset' => $charset ?? '', |
|
'is_show_stats' => $this->isShowStats, |
|
'ignored' => $ignored, |
|
'do' => $do, |
|
'approx_rows' => $approxRows, |
|
'show_superscript' => $showSuperscript, |
|
'already_favorite' => $this->checkFavoriteTable($currentTable['TABLE_NAME']), |
|
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'], |
|
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'], |
|
'limit_chars' => $GLOBALS['cfg']['LimitChars'], |
|
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'], |
|
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'], |
|
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'], |
|
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'], |
|
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'], |
|
]; |
|
|
|
$overallApproxRows = $overallApproxRows || $approxRows; |
|
} |
|
|
|
$databaseCollation = []; |
|
$databaseCharset = ''; |
|
$collation = Charsets::findCollationByName( |
|
$this->dbi, |
|
$GLOBALS['cfg']['Server']['DisableIS'], |
|
$this->dbi->getDbCollation($this->db) |
|
); |
|
if ($collation !== null) { |
|
$databaseCollation = [ |
|
'name' => $collation->getName(), |
|
'description' => $collation->getDescription(), |
|
]; |
|
$databaseCharset = $collation->getCharset(); |
|
} |
|
|
|
$relationParameters = $this->relation->getRelationParameters(); |
|
|
|
return $html . $this->template->render('database/structure/table_header', [ |
|
'db' => $this->db, |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
|
'replication' => $replicaInfo['status'], |
|
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'], |
|
'is_show_stats' => $this->isShowStats, |
|
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'], |
|
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'], |
|
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'], |
|
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'], |
|
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'], |
|
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'], |
|
'structure_table_rows' => $structureTableRows, |
|
'body_for_table_summary' => [ |
|
'num_tables' => $this->numTables, |
|
'server_replica_status' => $replicaInfo['status'], |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
|
'sum_entries' => $sumEntries, |
|
'database_collation' => $databaseCollation, |
|
'is_show_stats' => $this->isShowStats, |
|
'database_charset' => $databaseCharset, |
|
'sum_size' => $sumSize, |
|
'overhead_size' => $overheadSize, |
|
'create_time_all' => $createTimeAll ? Util::localisedDate(strtotime($createTimeAll)) : '-', |
|
'update_time_all' => $updateTimeAll ? Util::localisedDate(strtotime($updateTimeAll)) : '-', |
|
'check_time_all' => $checkTimeAll ? Util::localisedDate(strtotime($checkTimeAll)) : '-', |
|
'approx_rows' => $overallApproxRows, |
|
'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'], |
|
'db' => $GLOBALS['db'], |
|
'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'], |
|
'dbi' => $this->dbi, |
|
'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'], |
|
'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'], |
|
'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'], |
|
'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'], |
|
'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'], |
|
], |
|
'check_all_tables' => [ |
|
'text_dir' => $GLOBALS['text_dir'], |
|
'overhead_check' => $overheadCheck, |
|
'db_is_system_schema' => $this->dbIsSystemSchema, |
|
'hidden_fields' => $hiddenFields, |
|
'disable_multi_table' => $GLOBALS['cfg']['DisableMultiTableMaintenance'], |
|
'central_columns_work' => $relationParameters->centralColumnsFeature !== null, |
|
], |
|
]); |
|
} |
|
|
|
/** |
|
* Returns the tracking icon if the table is tracked |
|
* |
|
* @param string $table table name |
|
* |
|
* @return string HTML for tracking icon |
|
*/ |
|
protected function getTrackingIcon(string $table): string |
|
{ |
|
$trackingIcon = ''; |
|
if (Tracker::isActive()) { |
|
$isTracked = Tracker::isTracked($this->db, $table); |
|
if ($isTracked || Tracker::getVersion($this->db, $table) > 0) { |
|
$trackingIcon = $this->template->render('database/structure/tracking_icon', [ |
|
'db' => $this->db, |
|
'table' => $table, |
|
'is_tracked' => $isTracked, |
|
]); |
|
} |
|
} |
|
|
|
return $trackingIcon; |
|
} |
|
|
|
/** |
|
* Returns whether the row count is approximated |
|
* |
|
* @param array $currentTable array containing details about the table |
|
* @param bool $tableIsView whether the table is a view |
|
* |
|
* @return array |
|
*/ |
|
protected function isRowCountApproximated( |
|
array $currentTable, |
|
bool $tableIsView |
|
): array { |
|
$approxRows = false; |
|
$showSuperscript = ''; |
|
|
|
// there is a null value in the ENGINE |
|
// - when the table needs to be repaired, or |
|
// - when it's a view |
|
// so ensure that we'll display "in use" below for a table |
|
// that needs to be repaired |
|
if (isset($currentTable['TABLE_ROWS']) && ($currentTable['ENGINE'] != null || $tableIsView)) { |
|
// InnoDB/TokuDB table: we did not get an accurate row count |
|
$approxRows = ! $tableIsView |
|
&& in_array($currentTable['ENGINE'], ['InnoDB', 'TokuDB']) |
|
&& ! $currentTable['COUNTED']; |
|
|
|
if ($tableIsView && $currentTable['TABLE_ROWS'] >= $GLOBALS['cfg']['MaxExactCountViews']) { |
|
$approxRows = true; |
|
$showSuperscript = Generator::showHint( |
|
Sanitize::sanitizeMessage( |
|
sprintf( |
|
__( |
|
'This view has at least this number of rows. Please refer to %sdocumentation%s.' |
|
), |
|
'[doc@cfg_MaxExactCountViews]', |
|
'[/doc]' |
|
) |
|
) |
|
); |
|
} |
|
} |
|
|
|
return [ |
|
$approxRows, |
|
$showSuperscript, |
|
]; |
|
} |
|
|
|
/** |
|
* Returns the replication status of the table. |
|
* |
|
* @param array $replicaInfo |
|
* @param string $table table name |
|
* |
|
* @return array |
|
*/ |
|
protected function getReplicationStatus($replicaInfo, string $table): array |
|
{ |
|
$do = $ignored = false; |
|
if ($replicaInfo['status']) { |
|
$nbServReplicaDoDb = count($replicaInfo['Do_DB']); |
|
$nbServReplicaIgnoreDb = count($replicaInfo['Ignore_DB']); |
|
$searchDoDBInTruename = array_search($table, $replicaInfo['Do_DB']); |
|
$searchDoDBInDB = array_search($this->db, $replicaInfo['Do_DB']); |
|
|
|
$do = (is_string($searchDoDBInTruename) && strlen($searchDoDBInTruename) > 0) |
|
|| (is_string($searchDoDBInDB) && strlen($searchDoDBInDB) > 0) |
|
|| ($nbServReplicaDoDb == 0 && $nbServReplicaIgnoreDb == 0) |
|
|| $this->hasTable($replicaInfo['Wild_Do_Table'], $table); |
|
|
|
$searchDb = array_search($this->db, $replicaInfo['Ignore_DB']); |
|
$searchTable = array_search($table, $replicaInfo['Ignore_Table']); |
|
$ignored = (is_string($searchTable) && strlen($searchTable) > 0) |
|
|| (is_string($searchDb) && strlen($searchDb) > 0) |
|
|| $this->hasTable($replicaInfo['Wild_Ignore_Table'], $table); |
|
} |
|
|
|
return [ |
|
$do, |
|
$ignored, |
|
]; |
|
} |
|
|
|
/** |
|
* Function to check if a table is already in favorite list. |
|
* |
|
* @param string $currentTable current table |
|
*/ |
|
protected function checkFavoriteTable(string $currentTable): bool |
|
{ |
|
// ensure $_SESSION['tmpval']['favoriteTables'] is initialized |
|
RecentFavoriteTable::getInstance('favorite'); |
|
$favoriteTables = $_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']] ?? []; |
|
foreach ($favoriteTables as $value) { |
|
if ($value['db'] == $this->db && $value['table'] == $currentTable) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Find table with truename |
|
* |
|
* @param array $db DB to look into |
|
* @param string $truename Table name |
|
*/ |
|
protected function hasTable(array $db, $truename): bool |
|
{ |
|
foreach ($db as $dbTable) { |
|
if ( |
|
$this->db == $this->replication->extractDbOrTable($dbTable) |
|
&& preg_match( |
|
'@^' . |
|
preg_quote(mb_substr($this->replication->extractDbOrTable($dbTable, 'table'), 0, -1), '@') . '@', |
|
$truename |
|
) |
|
) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/** |
|
* Get the value set for ENGINE table, |
|
* |
|
* @internal param bool $table_is_view whether table is view or not |
|
* |
|
* @param array $currentTable current table |
|
* @param int $sumSize total table size |
|
* @param int $overheadSize overhead size |
|
* |
|
* @return array |
|
*/ |
|
protected function getStuffForEngineTypeTable( |
|
array $currentTable, |
|
$sumSize, |
|
$overheadSize |
|
) { |
|
$formattedSize = '-'; |
|
$unit = ''; |
|
$formattedOverhead = ''; |
|
$overheadUnit = ''; |
|
$tableIsView = false; |
|
|
|
switch ($currentTable['ENGINE']) { |
|
// MyISAM, ISAM or Heap table: Row count, data size and index size |
|
// are accurate; data size is accurate for ARCHIVE |
|
case 'MyISAM': |
|
case 'ISAM': |
|
case 'HEAP': |
|
case 'MEMORY': |
|
case 'ARCHIVE': |
|
case 'Aria': |
|
case 'Maria': |
|
[ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit, |
|
$overheadSize, |
|
$sumSize, |
|
] = $this->getValuesForAriaTable( |
|
$currentTable, |
|
$sumSize, |
|
$overheadSize, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit |
|
); |
|
break; |
|
case 'InnoDB': |
|
case 'PBMS': |
|
case 'TokuDB': |
|
// InnoDB table: Row count is not accurate but data and index sizes are. |
|
// PBMS table in Drizzle: TABLE_ROWS is taken from table cache, |
|
// so it may be unavailable |
|
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForInnodbTable( |
|
$currentTable, |
|
$sumSize |
|
); |
|
break; |
|
// Mysql 5.0.x (and lower) uses MRG_MyISAM |
|
// and MySQL 5.1.x (and higher) uses MRG_MYISAM |
|
// Both are aliases for MERGE |
|
case 'MRG_MyISAM': |
|
case 'MRG_MYISAM': |
|
case 'MERGE': |
|
case 'BerkeleyDB': |
|
// Merge or BerkleyDB table: Only row count is accurate. |
|
if ($this->isShowStats) { |
|
$formattedSize = ' - '; |
|
$unit = ''; |
|
} |
|
|
|
break; |
|
// for a view, the ENGINE is sometimes reported as null, |
|
// or on some servers it's reported as "SYSTEM VIEW" |
|
case null: |
|
case 'SYSTEM VIEW': |
|
// possibly a view, do nothing |
|
break; |
|
case 'Mroonga': |
|
// The idea is to show the size only if Mroonga is available, |
|
// in other case the old unknown message will appear |
|
if (StorageEngine::hasMroongaEngine()) { |
|
[$currentTable, $formattedSize, $unit, $sumSize] = $this->getValuesForMroongaTable( |
|
$currentTable, |
|
$sumSize |
|
); |
|
break; |
|
} |
|
// no break, go to default case |
|
default: |
|
// Unknown table type. |
|
if ($this->isShowStats) { |
|
$formattedSize = __('unknown'); |
|
$unit = ''; |
|
} |
|
} |
|
|
|
if ($currentTable['TABLE_TYPE'] === 'VIEW' || $currentTable['TABLE_TYPE'] === 'SYSTEM VIEW') { |
|
// countRecords() takes care of $cfg['MaxExactCountViews'] |
|
$currentTable['TABLE_ROWS'] = $this->dbi |
|
->getTable($this->db, $currentTable['TABLE_NAME']) |
|
->countRecords(true); |
|
$tableIsView = true; |
|
} |
|
|
|
return [ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit, |
|
$overheadSize, |
|
$tableIsView, |
|
$sumSize, |
|
]; |
|
} |
|
|
|
/** |
|
* Get values for ARIA/MARIA tables |
|
* |
|
* @param array $currentTable current table |
|
* @param int $sumSize sum size |
|
* @param int $overheadSize overhead size |
|
* @param int $formattedSize formatted size |
|
* @param string $unit unit |
|
* @param int $formattedOverhead overhead formatted |
|
* @param string $overheadUnit overhead unit |
|
* |
|
* @return array |
|
*/ |
|
protected function getValuesForAriaTable( |
|
array $currentTable, |
|
$sumSize, |
|
$overheadSize, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit |
|
) { |
|
if ($this->dbIsSystemSchema) { |
|
$currentTable['Rows'] = $this->dbi |
|
->getTable($this->db, $currentTable['Name']) |
|
->countRecords(); |
|
} |
|
|
|
if ($this->isShowStats) { |
|
/** @var int $tblsize */ |
|
$tblsize = $currentTable['Data_length'] |
|
+ $currentTable['Index_length']; |
|
$sumSize += $tblsize; |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, $tblsize > 0 ? 1 : 0); |
|
if (isset($currentTable['Data_free']) && $currentTable['Data_free'] > 0) { |
|
[$formattedOverhead, $overheadUnit] = Util::formatByteDown( |
|
$currentTable['Data_free'], |
|
3, |
|
($currentTable['Data_free'] > 0 ? 1 : 0) |
|
); |
|
$overheadSize += $currentTable['Data_free']; |
|
} |
|
} |
|
|
|
return [ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$formattedOverhead, |
|
$overheadUnit, |
|
$overheadSize, |
|
$sumSize, |
|
]; |
|
} |
|
|
|
/** |
|
* Get values for InnoDB table |
|
* |
|
* @param array $currentTable current table |
|
* @param int $sumSize sum size |
|
* |
|
* @return array |
|
*/ |
|
protected function getValuesForInnodbTable( |
|
array $currentTable, |
|
$sumSize |
|
) { |
|
$formattedSize = $unit = ''; |
|
|
|
if ( |
|
(in_array($currentTable['ENGINE'], ['InnoDB', 'TokuDB']) |
|
&& $currentTable['TABLE_ROWS'] < $GLOBALS['cfg']['MaxExactCount']) |
|
|| ! isset($currentTable['TABLE_ROWS']) |
|
) { |
|
$currentTable['COUNTED'] = true; |
|
$currentTable['TABLE_ROWS'] = $this->dbi |
|
->getTable($this->db, $currentTable['TABLE_NAME']) |
|
->countRecords(true); |
|
} else { |
|
$currentTable['COUNTED'] = false; |
|
} |
|
|
|
if ($this->isShowStats) { |
|
/** @var int $tblsize */ |
|
$tblsize = $currentTable['Data_length'] |
|
+ $currentTable['Index_length']; |
|
$sumSize += $tblsize; |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, ($tblsize > 0 ? 1 : 0)); |
|
} |
|
|
|
return [ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$sumSize, |
|
]; |
|
} |
|
|
|
/** |
|
* Get values for Mroonga table |
|
* |
|
* @param array $currentTable current table |
|
* @param int $sumSize sum size |
|
* |
|
* @return array |
|
*/ |
|
protected function getValuesForMroongaTable( |
|
array $currentTable, |
|
int $sumSize |
|
): array { |
|
$formattedSize = ''; |
|
$unit = ''; |
|
|
|
if ($this->isShowStats) { |
|
/** @var int $tblsize */ |
|
$tblsize = $currentTable['Data_length'] + $currentTable['Index_length']; |
|
$sumSize += $tblsize; |
|
[$formattedSize, $unit] = Util::formatByteDown($tblsize, 3, ($tblsize > 0 ? 1 : 0)); |
|
} |
|
|
|
return [ |
|
$currentTable, |
|
$formattedSize, |
|
$unit, |
|
$sumSize, |
|
]; |
|
} |
|
}
|
|
|