Files
dafuweng-saiadmin6.x/server/db/fix_bt_backup_view_tables.php

128 lines
3.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* 解决宝塔面板备份偶发报错:
* “备份文件中缺少表: xxx__view_backup”
*
* 原因通常是:数据库存在 VIEW但宝塔的备份校验按“表”去匹配
* 或其内部会期望存在 xxx__view_backup 之类的“视图备份表”标记。
*
* 本脚本会:
* - 扫描当前库所有 VIEW
* - 为每个 VIEW 创建一个同名的备份表:<view_name>__view_backup
* - 在该表写入 SHOW CREATE VIEW 的结果(若权限不足则写入空串)
*
* 用法(在 server 目录执行):
* php db/fix_bt_backup_view_tables.php
*/
require_once __DIR__ . '/../vendor/autoload.php';
if (class_exists(\Dotenv\Dotenv::class) && is_file(dirname(__DIR__) . '/.env')) {
if (method_exists(\Dotenv\Dotenv::class, 'createUnsafeMutable')) {
\Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load();
} else {
\Dotenv\Dotenv::createMutable(dirname(__DIR__))->load();
}
}
$host = getenv('DB_HOST') ?: '127.0.0.1';
$port = getenv('DB_PORT') ?: '3306';
$dbName = getenv('DB_NAME') ?: '';
$user = getenv('DB_USER') ?: '';
$pass = getenv('DB_PASSWORD') ?: '';
if ($dbName === '' || $user === '') {
fwrite(STDERR, "Missing DB_NAME/DB_USER in .env\n");
exit(1);
}
$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$views = $pdo
->query("SELECT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME")
->fetchAll(PDO::FETCH_COLUMN);
if (!$views) {
echo "No views found; nothing to do.\n";
exit(0);
}
$created = 0;
$updated = 0;
$failed = 0;
foreach ($views as $viewName) {
$viewName = (string) $viewName;
$backupTable = $viewName . '__view_backup';
if (!preg_match('/^[a-zA-Z0-9_]+$/', $backupTable)) {
$failed++;
echo "[skip] invalid name: {$backupTable}\n";
continue;
}
try {
$pdo->exec(
"CREATE TABLE IF NOT EXISTS `{$backupTable}` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`view_name` VARCHAR(255) NOT NULL,
`create_sql` LONGTEXT NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_view_name` (`view_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
$created++;
} catch (Throwable $e) {
$failed++;
echo "[fail] create table {$backupTable}: " . $e->getMessage() . "\n";
continue;
}
$createSql = '';
try {
$stmt = $pdo->query("SHOW CREATE VIEW `{$viewName}`");
$row = $stmt ? $stmt->fetch(PDO::FETCH_ASSOC) : false;
if ($row) {
// SHOW CREATE VIEW 返回字段名可能是 "Create View" 或类似
foreach ($row as $k => $v) {
if (is_string($k) && stripos($k, 'create') !== false && is_string($v)) {
$createSql = $v;
break;
}
}
}
} catch (Throwable $e) {
// 权限不足也不阻断,只是留空,保证备份表存在
$createSql = '';
}
try {
$stmt = $pdo->prepare(
"INSERT INTO `{$backupTable}` (`view_name`, `create_sql`, `updated_at`)
VALUES (:view_name, :create_sql, :updated_at)
ON DUPLICATE KEY UPDATE
`create_sql` = VALUES(`create_sql`),
`updated_at` = VALUES(`updated_at`)"
);
$stmt->execute([
'view_name' => $viewName,
'create_sql' => $createSql,
'updated_at' => date('Y-m-d H:i:s'),
]);
$updated++;
echo "[ok] {$viewName} -> {$backupTable}\n";
} catch (Throwable $e) {
$failed++;
echo "[fail] upsert {$backupTable}: " . $e->getMessage() . "\n";
}
}
echo "Done. created={$created}, updated={$updated}, failed={$failed}\n";