PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/classes/Controllers/Database/OperationsController.php

http://github.com/phpmyadmin/phpmyadmin
PHP | 326 lines | 221 code | 59 blank | 46 comment | 47 complexity | ab4f018a2287d6ab324debc28872b372 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Controllers\Database;
  4. use PhpMyAdmin\Charsets;
  5. use PhpMyAdmin\CheckUserPrivileges;
  6. use PhpMyAdmin\DatabaseInterface;
  7. use PhpMyAdmin\Html\Generator;
  8. use PhpMyAdmin\Message;
  9. use PhpMyAdmin\Operations;
  10. use PhpMyAdmin\Plugins;
  11. use PhpMyAdmin\Plugins\Export\ExportSql;
  12. use PhpMyAdmin\Query\Utilities;
  13. use PhpMyAdmin\Relation;
  14. use PhpMyAdmin\RelationCleanup;
  15. use PhpMyAdmin\ResponseRenderer;
  16. use PhpMyAdmin\Template;
  17. use PhpMyAdmin\Url;
  18. use PhpMyAdmin\Util;
  19. use function __;
  20. use function count;
  21. use function mb_strtolower;
  22. use function strlen;
  23. /**
  24. * Handles miscellaneous database operations.
  25. */
  26. class OperationsController extends AbstractController
  27. {
  28. /** @var Operations */
  29. private $operations;
  30. /** @var CheckUserPrivileges */
  31. private $checkUserPrivileges;
  32. /** @var Relation */
  33. private $relation;
  34. /** @var RelationCleanup */
  35. private $relationCleanup;
  36. /** @var DatabaseInterface */
  37. private $dbi;
  38. public function __construct(
  39. ResponseRenderer $response,
  40. Template $template,
  41. string $db,
  42. Operations $operations,
  43. CheckUserPrivileges $checkUserPrivileges,
  44. Relation $relation,
  45. RelationCleanup $relationCleanup,
  46. DatabaseInterface $dbi
  47. ) {
  48. parent::__construct($response, $template, $db);
  49. $this->operations = $operations;
  50. $this->checkUserPrivileges = $checkUserPrivileges;
  51. $this->relation = $relation;
  52. $this->relationCleanup = $relationCleanup;
  53. $this->dbi = $dbi;
  54. }
  55. public function __invoke(): void
  56. {
  57. global $cfg, $db, $server, $sql_query, $move, $message, $tables_full, $errorUrl;
  58. global $export_sql_plugin, $views, $sqlConstratints, $local_query, $reload, $urlParams, $tables;
  59. global $total_num_tables, $sub_part, $tooltip_truename;
  60. global $db_collation, $tooltip_aliasname, $pos, $is_information_schema, $single_table, $num_tables;
  61. $this->checkUserPrivileges->getPrivileges();
  62. $this->addScriptFiles(['database/operations.js']);
  63. $sql_query = '';
  64. /**
  65. * Rename/move or copy database
  66. */
  67. if (strlen($db) > 0 && (! empty($_POST['db_rename']) || ! empty($_POST['db_copy']))) {
  68. if (! empty($_POST['db_rename'])) {
  69. $move = true;
  70. } else {
  71. $move = false;
  72. }
  73. if (! isset($_POST['newname']) || strlen($_POST['newname']) === 0) {
  74. $message = Message::error(__('The database name is empty!'));
  75. } else {
  76. // lower_case_table_names=1 `DB` becomes `db`
  77. if ($this->dbi->getLowerCaseNames() === '1') {
  78. $_POST['newname'] = mb_strtolower($_POST['newname']);
  79. }
  80. if ($_POST['newname'] === $_REQUEST['db']) {
  81. $message = Message::error(
  82. __('Cannot copy database to the same name. Change the name and try again.')
  83. );
  84. } else {
  85. $_error = false;
  86. if ($move || ! empty($_POST['create_database_before_copying'])) {
  87. $this->operations->createDbBeforeCopy();
  88. }
  89. // here I don't use DELIMITER because it's not part of the
  90. // language; I have to send each statement one by one
  91. // to avoid selecting alternatively the current and new db
  92. // we would need to modify the CREATE definitions to qualify
  93. // the db name
  94. $this->operations->runProcedureAndFunctionDefinitions($db);
  95. // go back to current db, just in case
  96. $this->dbi->selectDb($db);
  97. $tables_full = $this->dbi->getTablesFull($db);
  98. // remove all foreign key constraints, otherwise we can get errors
  99. /** @var ExportSql $export_sql_plugin */
  100. $export_sql_plugin = Plugins::getPlugin('export', 'sql', [
  101. 'export_type' => 'database',
  102. 'single_table' => isset($single_table),
  103. ]);
  104. // create stand-in tables for views
  105. $views = $this->operations->getViewsAndCreateSqlViewStandIn($tables_full, $export_sql_plugin, $db);
  106. // copy tables
  107. $sqlConstratints = $this->operations->copyTables($tables_full, $move, $db);
  108. // handle the views
  109. if (! $_error) {
  110. $this->operations->handleTheViews($views, $move, $db);
  111. }
  112. unset($views);
  113. // now that all tables exist, create all the accumulated constraints
  114. if (! $_error && count($sqlConstratints) > 0) {
  115. $this->operations->createAllAccumulatedConstraints($sqlConstratints);
  116. }
  117. unset($sqlConstratints);
  118. if ($this->dbi->getVersion() >= 50100) {
  119. // here DELIMITER is not used because it's not part of the
  120. // language; each statement is sent one by one
  121. $this->operations->runEventDefinitionsForDb($db);
  122. }
  123. // go back to current db, just in case
  124. $this->dbi->selectDb($db);
  125. // Duplicate the bookmarks for this db (done once for each db)
  126. $this->operations->duplicateBookmarks($_error, $db);
  127. if (! $_error && $move) {
  128. if (isset($_POST['adjust_privileges']) && ! empty($_POST['adjust_privileges'])) {
  129. $this->operations->adjustPrivilegesMoveDb($db, $_POST['newname']);
  130. }
  131. /**
  132. * cleanup pmadb stuff for this db
  133. */
  134. $this->relationCleanup->database($db);
  135. // if someday the RENAME DATABASE reappears, do not DROP
  136. $local_query = 'DROP DATABASE '
  137. . Util::backquote($db) . ';';
  138. $sql_query .= "\n" . $local_query;
  139. $this->dbi->query($local_query);
  140. $message = Message::success(
  141. __('Database %1$s has been renamed to %2$s.')
  142. );
  143. $message->addParam($db);
  144. $message->addParam($_POST['newname']);
  145. } elseif (! $_error) {
  146. if (isset($_POST['adjust_privileges']) && ! empty($_POST['adjust_privileges'])) {
  147. $this->operations->adjustPrivilegesCopyDb($db, $_POST['newname']);
  148. }
  149. $message = Message::success(
  150. __('Database %1$s has been copied to %2$s.')
  151. );
  152. $message->addParam($db);
  153. $message->addParam($_POST['newname']);
  154. } else {
  155. $message = Message::error();
  156. }
  157. $reload = true;
  158. /* Change database to be used */
  159. if (! $_error && $move) {
  160. $db = $_POST['newname'];
  161. } elseif (! $_error) {
  162. if (isset($_POST['switch_to_new']) && $_POST['switch_to_new'] === 'true') {
  163. $_SESSION['pma_switch_to_new'] = true;
  164. $db = $_POST['newname'];
  165. } else {
  166. $_SESSION['pma_switch_to_new'] = false;
  167. }
  168. }
  169. }
  170. }
  171. /**
  172. * Database has been successfully renamed/moved. If in an Ajax request,
  173. * generate the output with {@link ResponseRenderer} and exit
  174. */
  175. if ($this->response->isAjax()) {
  176. $this->response->setRequestStatus($message->isSuccess());
  177. $this->response->addJSON('message', $message);
  178. $this->response->addJSON('newname', $_POST['newname']);
  179. $this->response->addJSON(
  180. 'sql_query',
  181. Generator::getMessage('', $sql_query)
  182. );
  183. $this->response->addJSON('db', $db);
  184. return;
  185. }
  186. }
  187. /**
  188. * Settings for relations stuff
  189. */
  190. $cfgRelation = $this->relation->getRelationsParam();
  191. /**
  192. * Check if comments were updated
  193. * (must be done before displaying the menu tabs)
  194. */
  195. if (isset($_POST['comment'])) {
  196. $this->relation->setDbComment($db, $_POST['comment']);
  197. }
  198. Util::checkParameters(['db']);
  199. $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
  200. $errorUrl .= Url::getCommon(['db' => $db], '&');
  201. if (! $this->hasDatabase()) {
  202. return;
  203. }
  204. $urlParams['goto'] = Url::getFromRoute('/database/operations');
  205. // Gets the database structure
  206. $sub_part = '_structure';
  207. [
  208. $tables,
  209. $num_tables,
  210. $total_num_tables,
  211. $sub_part,,
  212. $isSystemSchema,
  213. $tooltip_truename,
  214. $tooltip_aliasname,
  215. $pos,
  216. ] = Util::getDbInfo($db, $sub_part ?? '');
  217. $oldMessage = '';
  218. if (isset($message)) {
  219. $oldMessage = Generator::getMessage($message, $sql_query);
  220. unset($message);
  221. }
  222. $db_collation = $this->dbi->getDbCollation($db);
  223. $is_information_schema = Utilities::isSystemSchema($db);
  224. if ($is_information_schema) {
  225. return;
  226. }
  227. $databaseComment = '';
  228. if ($cfgRelation['commwork']) {
  229. $databaseComment = $this->relation->getDbComment($db);
  230. }
  231. $hasAdjustPrivileges = $GLOBALS['db_priv'] && $GLOBALS['table_priv']
  232. && $GLOBALS['col_priv'] && $GLOBALS['proc_priv'] && $GLOBALS['is_reload_priv'];
  233. $isDropDatabaseAllowed = ($this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'])
  234. && ! $isSystemSchema && $db !== 'mysql';
  235. $switchToNew = isset($_SESSION['pma_switch_to_new']) && $_SESSION['pma_switch_to_new'];
  236. $charsets = Charsets::getCharsets($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  237. $collations = Charsets::getCollations($this->dbi, $GLOBALS['cfg']['Server']['DisableIS']);
  238. if (! $cfgRelation['allworks'] && $cfg['PmaNoRelation_DisableWarning'] == false) {
  239. $message = Message::notice(
  240. __(
  241. 'The phpMyAdmin configuration storage has been deactivated. %sFind out why%s.'
  242. )
  243. );
  244. $message->addParamHtml(
  245. '<a href="' . Url::getFromRoute('/check-relations')
  246. . '" data-post="' . Url::getCommon(['db' => $db]) . '">'
  247. );
  248. $message->addParamHtml('</a>');
  249. /* Show error if user has configured something, notice elsewhere */
  250. if (! empty($cfg['Servers'][$server]['pmadb'])) {
  251. $message->isError(true);
  252. }
  253. }
  254. $this->render('database/operations/index', [
  255. 'message' => $oldMessage,
  256. 'db' => $db,
  257. 'has_comment' => $cfgRelation['commwork'],
  258. 'db_comment' => $databaseComment,
  259. 'db_collation' => $db_collation,
  260. 'has_adjust_privileges' => $hasAdjustPrivileges,
  261. 'is_drop_database_allowed' => $isDropDatabaseAllowed,
  262. 'switch_to_new' => $switchToNew,
  263. 'charsets' => $charsets,
  264. 'collations' => $collations,
  265. ]);
  266. }
  267. }