123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- <?php
- declare(strict_types=1);
- namespace PhpMyAdmin\Controllers\Server;
- use PhpMyAdmin\Charsets;
- use PhpMyAdmin\Charsets\Charset;
- use PhpMyAdmin\Charsets\Collation;
- use PhpMyAdmin\CheckUserPrivileges;
- use PhpMyAdmin\Controllers\AbstractController;
- use PhpMyAdmin\DatabaseInterface;
- use PhpMyAdmin\Html\Generator;
- use PhpMyAdmin\Message;
- use PhpMyAdmin\Query\Utilities;
- use PhpMyAdmin\RelationCleanup;
- use PhpMyAdmin\ReplicationInfo;
- use PhpMyAdmin\Response;
- use PhpMyAdmin\Template;
- use PhpMyAdmin\Transformations;
- use PhpMyAdmin\Url;
- use PhpMyAdmin\Util;
- use function array_key_exists;
- use function array_keys;
- use function array_search;
- use function count;
- use function explode;
- use function in_array;
- use function mb_strlen;
- use function mb_strtolower;
- use function strlen;
- use function strpos;
- /**
- * Handles viewing and creating and deleting databases
- */
- class DatabasesController extends AbstractController
- {
- /** @var array array of database details */
- private $databases = [];
- /** @var int number of databases */
- private $databaseCount = 0;
- /** @var string sort by column */
- private $sortBy;
- /** @var string sort order of databases */
- private $sortOrder;
- /** @var bool whether to show database statistics */
- private $hasStatistics;
- /** @var int position in list navigation */
- private $position;
- /** @var Transformations */
- private $transformations;
- /** @var RelationCleanup */
- private $relationCleanup;
- /** @var DatabaseInterface */
- private $dbi;
- /**
- * @param Response $response
- * @param DatabaseInterface $dbi
- */
- public function __construct(
- $response,
- Template $template,
- Transformations $transformations,
- RelationCleanup $relationCleanup,
- $dbi
- ) {
- parent::__construct($response, $template);
- $this->transformations = $transformations;
- $this->relationCleanup = $relationCleanup;
- $this->dbi = $dbi;
- $checkUserPrivileges = new CheckUserPrivileges($dbi);
- $checkUserPrivileges->getPrivileges();
- }
- public function index(): void
- {
- global $cfg, $server, $dblist, $is_create_db_priv;
- global $db_to_create, $text_dir, $PMA_Theme, $err_url;
- $params = [
- 'statistics' => $_REQUEST['statistics'] ?? null,
- 'pos' => $_REQUEST['pos'] ?? null,
- 'sort_by' => $_REQUEST['sort_by'] ?? null,
- 'sort_order' => $_REQUEST['sort_order'] ?? null,
- ];
- $this->addScriptFiles(['server/databases.js']);
- $err_url = Url::getFromRoute('/');
- if ($this->dbi->isSuperUser()) {
- $this->dbi->selectDb('mysql');
- }
- $replicationInfo = new ReplicationInfo($this->dbi);
- $replicationInfo->load($_POST['master_connection'] ?? null);
- $primaryInfo = $replicationInfo->getPrimaryInfo();
- $replicaInfo = $replicationInfo->getReplicaInfo();
- $this->setSortDetails($params['sort_by'], $params['sort_order']);
- $this->hasStatistics = ! empty($params['statistics']);
- $this->position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
- /**
- * Gets the databases list
- */
- if ($server > 0) {
- $this->databases = $this->dbi->getDatabasesFull(
- null,
- $this->hasStatistics,
- DatabaseInterface::CONNECT_USER,
- $this->sortBy,
- $this->sortOrder,
- $this->position,
- true
- );
- $this->databaseCount = count($dblist->databases);
- }
- $urlParams = [
- 'statistics' => $this->hasStatistics,
- 'pos' => $this->position,
- 'sort_by' => $this->sortBy,
- 'sort_order' => $this->sortOrder,
- ];
- $databases = $this->getDatabases($primaryInfo, $replicaInfo);
- $charsetsList = [];
- if ($cfg['ShowCreateDb'] && $is_create_db_priv) {
- $charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
- $collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
- $serverCollation = $this->dbi->getServerCollation();
- /** @var Charset $charset */
- foreach ($charsets as $charset) {
- $collationsList = [];
- /** @var Collation $collation */
- foreach ($collations[$charset->getName()] as $collation) {
- $collationsList[] = [
- 'name' => $collation->getName(),
- 'description' => $collation->getDescription(),
- 'is_selected' => $serverCollation === $collation->getName(),
- ];
- }
- $charsetsList[] = [
- 'name' => $charset->getName(),
- 'description' => $charset->getDescription(),
- 'collations' => $collationsList,
- ];
- }
- }
- $headerStatistics = $this->getStatisticsColumns();
- $this->render('server/databases/index', [
- 'is_create_database_shown' => $cfg['ShowCreateDb'],
- 'has_create_database_privileges' => $is_create_db_priv,
- 'has_statistics' => $this->hasStatistics,
- 'database_to_create' => $db_to_create,
- 'databases' => $databases['databases'],
- 'total_statistics' => $databases['total_statistics'],
- 'header_statistics' => $headerStatistics,
- 'charsets' => $charsetsList,
- 'database_count' => $this->databaseCount,
- 'pos' => $this->position,
- 'url_params' => $urlParams,
- 'max_db_list' => $cfg['MaxDbList'],
- 'has_master_replication' => $primaryInfo['status'],
- 'has_slave_replication' => $replicaInfo['status'],
- 'is_drop_allowed' => $this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'],
- 'theme_image_path' => $PMA_Theme->getImgPath(),
- 'text_dir' => $text_dir,
- ]);
- }
- public function create(): void
- {
- global $cfg, $db;
- $params = [
- 'new_db' => $_POST['new_db'] ?? null,
- 'db_collation' => $_POST['db_collation'] ?? null,
- ];
- if (! isset($params['new_db']) || mb_strlen($params['new_db']) === 0 || ! $this->response->isAjax()) {
- $this->response->addJSON(['message' => Message::error()]);
- return;
- }
- // lower_case_table_names=1 `DB` becomes `db`
- if ($this->dbi->getLowerCaseNames() === '1') {
- $params['new_db'] = mb_strtolower(
- $params['new_db']
- );
- }
- /**
- * Builds and executes the db creation sql query
- */
- $sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']);
- if (! empty($params['db_collation'])) {
- [$databaseCharset] = explode('_', $params['db_collation']);
- $charsets = Charsets::getCharsets(
- $this->dbi,
- $cfg['Server']['DisableIS']
- );
- $collations = Charsets::getCollations(
- $this->dbi,
- $cfg['Server']['DisableIS']
- );
- if (array_key_exists($databaseCharset, $charsets)
- && array_key_exists($params['db_collation'], $collations[$databaseCharset])
- ) {
- $sqlQuery .= ' DEFAULT'
- . Util::getCharsetQueryPart($params['db_collation']);
- }
- }
- $sqlQuery .= ';';
- $result = $this->dbi->tryQuery($sqlQuery);
- if (! $result) {
- // avoid displaying the not-created db name in header or navi panel
- $db = '';
- $message = Message::rawError((string) $this->dbi->getError());
- $json = ['message' => $message];
- $this->response->setRequestStatus(false);
- } else {
- $db = $params['new_db'];
- $message = Message::success(__('Database %1$s has been created.'));
- $message->addParam($params['new_db']);
- $scriptName = Util::getScriptNameForOption(
- $cfg['DefaultTabDatabase'],
- 'database'
- );
- $json = [
- 'message' => $message,
- 'sql_query' => Generator::getMessage('', $sqlQuery, 'success'),
- 'url' => $scriptName . Url::getCommon(
- ['db' => $params['new_db']],
- strpos($scriptName, '?') === false ? '?' : '&'
- ),
- ];
- }
- $this->response->addJSON($json);
- }
- /**
- * Handles dropping multiple databases
- */
- public function destroy(): void
- {
- global $selected, $err_url, $cfg, $dblist, $reload;
- $params = [
- 'drop_selected_dbs' => $_POST['drop_selected_dbs'] ?? null,
- 'selected_dbs' => $_POST['selected_dbs'] ?? null,
- ];
- /** @var Message|int $message */
- $message = -1;
- if (! isset($params['drop_selected_dbs'])
- || ! $this->response->isAjax()
- || (! $this->dbi->isSuperUser() && ! $cfg['AllowUserDropDatabase'])
- ) {
- $message = Message::error();
- $json = ['message' => $message];
- $this->response->setRequestStatus($message->isSuccess());
- $this->response->addJSON($json);
- return;
- }
- if (! isset($params['selected_dbs'])) {
- $message = Message::error(__('No databases selected.'));
- $json = ['message' => $message];
- $this->response->setRequestStatus($message->isSuccess());
- $this->response->addJSON($json);
- return;
- }
- $err_url = Url::getFromRoute('/server/databases');
- $selected = $_POST['selected_dbs'];
- $rebuildDatabaseList = false;
- $sqlQuery = '';
- $numberOfDatabases = count($selected);
- for ($i = 0; $i < $numberOfDatabases; $i++) {
- $this->relationCleanup->database($selected[$i]);
- $aQuery = 'DROP DATABASE ' . Util::backquote($selected[$i]);
- $reload = true;
- $rebuildDatabaseList = true;
- $sqlQuery .= $aQuery . ';' . "\n";
- $this->dbi->query($aQuery);
- $this->transformations->clear($selected[$i]);
- }
- if ($rebuildDatabaseList) {
- $dblist->databases->build();
- }
- if ($message === -1) { // no error message
- $message = Message::success(
- _ngettext(
- '%1$d database has been dropped successfully.',
- '%1$d databases have been dropped successfully.',
- $numberOfDatabases
- )
- );
- $message->addParam($numberOfDatabases);
- }
- $json = [];
- if ($message instanceof Message) {
- $json = ['message' => $message];
- $this->response->setRequestStatus($message->isSuccess());
- }
- $this->response->addJSON($json);
- }
- /**
- * Extracts parameters sort order and sort by
- *
- * @param string|null $sortBy sort by
- * @param string|null $sortOrder sort order
- */
- private function setSortDetails(?string $sortBy, ?string $sortOrder): void
- {
- if (empty($sortBy)) {
- $this->sortBy = 'SCHEMA_NAME';
- } else {
- $sortByAllowList = [
- 'SCHEMA_NAME',
- 'DEFAULT_COLLATION_NAME',
- 'SCHEMA_TABLES',
- 'SCHEMA_TABLE_ROWS',
- 'SCHEMA_DATA_LENGTH',
- 'SCHEMA_INDEX_LENGTH',
- 'SCHEMA_LENGTH',
- 'SCHEMA_DATA_FREE',
- ];
- $this->sortBy = 'SCHEMA_NAME';
- if (in_array($sortBy, $sortByAllowList)) {
- $this->sortBy = $sortBy;
- }
- }
- $this->sortOrder = 'asc';
- if (! isset($sortOrder)
- || mb_strtolower($sortOrder) !== 'desc'
- ) {
- return;
- }
- $this->sortOrder = 'desc';
- }
- /**
- * @param array $primaryInfo
- * @param array $replicaInfo
- *
- * @return array
- */
- private function getDatabases($primaryInfo, $replicaInfo): array
- {
- global $cfg;
- $databases = [];
- $totalStatistics = $this->getStatisticsColumns();
- foreach ($this->databases as $database) {
- $replication = [
- 'master' => ['status' => $primaryInfo['status']],
- 'slave' => ['status' => $replicaInfo['status']],
- ];
- if ($primaryInfo['status']) {
- $key = array_search($database['SCHEMA_NAME'], $primaryInfo['Ignore_DB']);
- $replication['master']['is_replicated'] = false;
- if (strlen((string) $key) === 0) {
- $key = array_search($database['SCHEMA_NAME'], $primaryInfo['Do_DB']);
- if (strlen((string) $key) > 0 || count($primaryInfo['Do_DB']) === 0) {
- $replication['master']['is_replicated'] = true;
- }
- }
- }
- if ($replicaInfo['status']) {
- $key = array_search($database['SCHEMA_NAME'], $replicaInfo['Ignore_DB']);
- $replication['slave']['is_replicated'] = false;
- if (strlen((string) $key) === 0) {
- $key = array_search($database['SCHEMA_NAME'], $replicaInfo['Do_DB']);
- if (strlen((string) $key) > 0 || count($replicaInfo['Do_DB']) === 0) {
- $replication['slave']['is_replicated'] = true;
- }
- }
- }
- $statistics = $this->getStatisticsColumns();
- if ($this->hasStatistics) {
- foreach (array_keys($statistics) as $key) {
- $statistics[$key]['raw'] = $database[$key] ?? null;
- $totalStatistics[$key]['raw'] += (int) $database[$key] ?? 0;
- }
- }
- $url = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
- $url .= Url::getCommonRaw(
- ['db' => $database['SCHEMA_NAME']],
- strpos($url, '?') === false ? '?' : '&'
- );
- $databases[$database['SCHEMA_NAME']] = [
- 'name' => $database['SCHEMA_NAME'],
- 'collation' => [],
- 'statistics' => $statistics,
- 'replication' => $replication,
- 'is_system_schema' => Utilities::isSystemSchema(
- $database['SCHEMA_NAME'],
- true
- ),
- 'is_pmadb' => $database['SCHEMA_NAME'] === ($cfg['Server']['pmadb'] ?? ''),
- 'url' => $url,
- ];
- $collation = Charsets::findCollationByName(
- $this->dbi,
- $cfg['Server']['DisableIS'],
- $database['DEFAULT_COLLATION_NAME']
- );
- if ($collation === null) {
- continue;
- }
- $databases[$database['SCHEMA_NAME']]['collation'] = [
- 'name' => $collation->getName(),
- 'description' => $collation->getDescription(),
- ];
- }
- return [
- 'databases' => $databases,
- 'total_statistics' => $totalStatistics,
- ];
- }
- /**
- * Prepares the statistics columns
- *
- * @return array
- */
- private function getStatisticsColumns(): array
- {
- return [
- 'SCHEMA_TABLES' => [
- 'title' => __('Tables'),
- 'format' => 'number',
- 'raw' => 0,
- ],
- 'SCHEMA_TABLE_ROWS' => [
- 'title' => __('Rows'),
- 'format' => 'number',
- 'raw' => 0,
- ],
- 'SCHEMA_DATA_LENGTH' => [
- 'title' => __('Data'),
- 'format' => 'byte',
- 'raw' => 0,
- ],
- 'SCHEMA_INDEX_LENGTH' => [
- 'title' => __('Indexes'),
- 'format' => 'byte',
- 'raw' => 0,
- ],
- 'SCHEMA_LENGTH' => [
- 'title' => __('Total'),
- 'format' => 'byte',
- 'raw' => 0,
- ],
- 'SCHEMA_DATA_FREE' => [
- 'title' => __('Overhead'),
- 'format' => 'byte',
- 'raw' => 0,
- ],
- ];
- }
- }
|