diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 8603a3287d0..fdb504b2a72 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -4,6 +4,7 @@ - Window scrolling is now blocked when a modal window is open. ([#16768](https://github.com/craftcms/cms/pull/16768)) ### Administration +- Added the `db/repair` command. ([#16812](https://github.com/craftcms/cms/pull/16812)) - Added the `--batch-size` option for `resave/*` commands. ([#16586](https://github.com/craftcms/cms/issues/16586)) - The `users/create` command now prompts to send an activation email, or outputs an activation URL. ([#16794](https://github.com/craftcms/cms/pull/16794)) - Dragging headings within the Customize Sources modal now also drags any subsequent sources. ([#16737](https://github.com/craftcms/cms/issues/16737)) diff --git a/src/console/controllers/DbController.php b/src/console/controllers/DbController.php index 31aa7de4505..c2911721463 100644 --- a/src/console/controllers/DbController.php +++ b/src/console/controllers/DbController.php @@ -82,6 +82,40 @@ public function options($actionID): array return $options; } + /** + * Repairs all tables in the database. + * + * Note that this can cause table locking, which could interfere with SQL being executed. + * + * @since 4.15.0 + * @see https://dev.mysql.com/doc/refman/8.4/en/optimize-table.html + * @see https://www.postgresql.org/docs/current/sql-analyze.html + */ + public function actionRepair(): int + { + if (!$this->_tablesExist()) { + $this->stdout('No existing database tables found.' . PHP_EOL, Console::FG_YELLOW); + return ExitCode::OK; + } + + if ($this->interactive && !$this->confirm('Are you sure you want to repair all tables from the database?')) { + $this->stdout('Aborted.' . PHP_EOL, Console::FG_YELLOW); + return ExitCode::OK; + } + + $this->_backupPrompt(); + + try { + $this->_repairAllTables(); + } catch (Throwable $e) { + Craft::$app->getErrorHandler()->logException($e); + $this->stderr('error: ' . $e->getMessage() . PHP_EOL, Console::FG_RED); + return ExitCode::UNSPECIFIED_ERROR; + } + + return ExitCode::OK; + } + /** * Drops all tables in the database. * @@ -140,6 +174,34 @@ private function _backupPrompt(): void } } + /** + * Repairs all tables in the database. + * + * @throws NotSupportedException + * @throws Exception + */ + private function _repairAllTables(): void + { + $db = Craft::$app->getDb(); + $tableNames = $db->getSchema()->getTableNames(); + + $this->stdout('Repairing all database tables ... ' . PHP_EOL); + + if ($db->getIsMysql()) { + $sql = 'OPTIMIZE TABLE [[%s]]'; + } else { + $sql = 'ANALYZE VERBOSE [[%s]]'; + } + + foreach ($tableNames as $tableName) { + $this->do("Repairing `$tableName`", function() use ($db, $sql, $tableName) { + $db->createCommand(sprintf($sql, $tableName))->execute(); + }); + } + + $this->stdout('Finished repairing all database tables.' . PHP_EOL . PHP_EOL, Console::FG_GREEN); + } + /** * Drops all tables in the database. *