From 69e4fe98477312cefd93e901284f5a1a8ccebd98 Mon Sep 17 00:00:00 2001 From: arabcoders Date: Fri, 9 May 2025 21:46:17 +0300 Subject: [PATCH] Add RepairCommand for database repair functionality --- src/Commands/Database/RepairCommand.php | 120 ++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 src/Commands/Database/RepairCommand.php diff --git a/src/Commands/Database/RepairCommand.php b/src/Commands/Database/RepairCommand.php new file mode 100644 index 00000000..ee5eba7b --- /dev/null +++ b/src/Commands/Database/RepairCommand.php @@ -0,0 +1,120 @@ +setName(self::ROUTE) + ->setDescription('Attempt to repair broken database.') + ->addArgument('db', InputOption::VALUE_REQUIRED, 'Database to repair.'); + } + + /** + * Execute the command. + * + * @param iInput $input The input object. + * @param iOutput $output The output object. + * + * @return int The command's exit code. + */ + protected function runCommand(iInput $input, iOutput $output): int + { + $db = $input->getArgument('db'); + if (empty($db)) { + $output->writeln('ERROR: You need to provide path to the sqlite db.'); + return self::FAILURE; + } + + if (false === file_exists($db)) { + $output->writeln(r("ERROR: Database '{db}' not found.", ['db' => $db])); + return self::FAILURE; + } + + $output->writeln(r("INFO: Attempting to repair database '{db}'.", ['db' => $db])); + + // -- first copy db to prevent data loss. + $backup = $db . '.before.repair.db'; + if (false === copy($db, $backup)) { + $output->writeln(r("ERROR: Failed to copy database '{db}' to '{backup}'.", [ + 'db' => $db, + 'backup' => $backup, + ])); + return self::FAILURE; + } + + $output->writeln(r("INFO: Copied database '{db}' to '{backup}' as backup.", [ + 'db' => $db, + 'backup' => $backup, + ])); + + $output->writeln(r("INFO: Attempting to repair database '{db}'.", ['db' => $db])); + + $command = "sqlite3 '{file}' '.dump' | sqlite3 '{file}.new.db'"; + $proc = Process::fromShellCommandline(r($command, ['file' => $db])); + $proc->setTimeout(null); + $proc->setIdleTimeout(null); + $proc->run(fn($type, $out) => $output->writeln((string)$out)); + if ($proc->isSuccessful()) { + $output->writeln(r("INFO: Database '{db}' repaired successfully.", ['db' => $db])); + } else { + $output->writeln(r("ERROR: Failed to repair database '{db}'.", ['db' => $db])); + return self::FAILURE; + } + + $command = "sqlite3 '{file}.new.db' 'PRAGMA integrity_check'"; + $proc = Process::fromShellCommandline(r($command, ['file' => $db])); + $proc->setTimeout(null); + $proc->setIdleTimeout(null); + $output->writeln('INFO: Checking database integrity...'); + $proc->run(fn($type, $out) => $output->writeln((string)$out)); + if ($proc->isSuccessful()) { + $output->writeln(r("INFO: Database '{db}' is valid.", ['db' => $db])); + } else { + $output->writeln(r("ERROR: Database '{db}' is not valid.", ['db' => $db])); + return self::FAILURE; + } + + if (!rename("{$db}.new.db", $db)) { + $output->writeln(r("ERROR: Failed to rename database '{db}.new.db' to '{db}'.", [ + 'db' => $db, + ])); + return self::FAILURE; + } + + $output->writeln(r("INFO: Done!.", ['db' => $db])); + + return self::SUCCESS; + } +}