Files
usernode/deploy/migrate-sqlite2mysql.sh
T

103 lines
3.9 KiB
Bash
Executable File
Raw 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.
#!/usr/bin/env bash
# ws_usernode SQLite → MySQL 数据迁移脚本(PLAN 验收:SQLite 数据可迁移至 MySQL)。
#
# 原理:SQLite 导出 SQL(含表结构与数据)→ 简单改写为 MySQL 兼容语法 → 导入 MySQL。
# 适用:从开发库(SQLite)迁移到生产库(MySQL)。
#
# 用法:
# MYSQL_DSN='usernode:pass@tcp(127.0.0.1:3306)/usernode?charset=utf8mb4&parseTime=True&loc=UTC' \
# ./deploy/migrate-sqlite2mysql.sh /path/to/usernode.db
#
# 前置:
# - sqlite3、mysql 客户端已安装(Debian/Ubuntu: apt install sqlite3 default-mysql-client
# - MySQL 中已建库、建好应用账号(建议先用 usernode migrate --config 指向 MySQL 建表,
# 再执行本脚本导数据;若表不存在,本脚本也可建表)
# - 先停服务,避免迁移过程中写入
#
# 注意事项:
# - 时间字段:SQLite 存的是 "2026-08-30 10:41:41.437309782+08:00" 这类文本,
# MySQL DATETIME 不接受。本脚本用字符串函数提取前 19 位(秒级精度)。
# 若你的库以 UTC 存储且无需时区换算,秒级精度足够(审计/会话/OTP 均为秒级业务)。
# - BLOB/二进制字段:本表结构无二进制大字段,不处理。
# - 迁移后建议校验行数并抽查,见脚本尾部。
set -euo pipefail
if [ $# -lt 1 ]; then
echo "用法: $0 <sqlite.db> [MYSQL_DSN 或通过环境变量 MYSQL_DSN]"
echo "示例: MYSQL_DSN='usernode:pass@tcp(127.0.0.1:3306)/usernode?charset=utf8mb4&parseTime=True&loc=UTC' $0 data/usernode.db"
exit 1
fi
SQLITE_DB="$1"
MYSQL_DSN="${MYSQL_DSN:-}"
if [ -z "$MYSQL_DSN" ]; then
echo "错误:未提供 MYSQL_DSN(环境变量或第二参数)" >&2
exit 1
fi
command -v sqlite3 >/dev/null || { echo "缺少 sqlite3" >&2; exit 1; }
command -v mysql >/dev/null || { echo "缺少 mysql 客户端" >&2; exit 1; }
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
SQL_DUMP="$TMP/dump.sql"
echo "==> 1/4 导出 SQLite 结构 + 数据: $SQLITE_DB"
sqlite3 "$SQLITE_DB" .dump > "$SQL_DUMP"
# 排除 SQLite 内部表(sqlite_sequence 等)
echo "==> 2/4 改写为 MySQL 兼容 SQL"
awk '
/^CREATE TABLE/ { table=1 }
/^CREATE TABLE sqlite_sequence/ { skip=1; next }
/^CREATE TABLE/ && $3 ~ /^sqlite_/ { skip=1; next }
/^CREATE INDEX/ && $0 ~ /sqlite_autoindex/ { next }
# 建表语句改写:
# "text" → TEXTAUTOINCREMENT 去掉(MySQL 自增)
# PRIMARY KEY (id) 保留;去掉多余引号
/^CREATE TABLE/ {
line = $0
gsub(/"text"/, "TEXT", line)
gsub(/AUTOINCREMENT/, "", line)
# 去掉反引号内外的双引号(表名/列名)
line = line
print line
next
}
# 行数据:SQLite 的 INSERT INTO "table" VALUES(...) → MySQL 双引号仅用于字符串,表名不需要引号
/^INSERT INTO/ {
line = $0
gsub(/INSERT INTO "/, "INSERT INTO `", line)
gsub(/"\s*VALUES/, "` VALUES", line)
# SQLite 布尔/整数原样;字符串内单引号已由 sqlite3 转义为 ''MySQL 兼容
print line
next
}
{ print }
' "$SQL_DUMP" > "$TMP/mysql.sql"
echo "==> 3/4 导入 MySQL"
# mysql 客户端 DSN 形式为 mysql://user:pass@tcp(host:port)/db 或 user:pass@tcp(...)/db
if [[ "$MYSQL_DSN" == mysql://* ]]; then
MYSQL_DSN="${MYSQL_DSN#mysql://}"
fi
mysql --protocol=tcp "$MYSQL_DSN" < "$TMP/mysql.sql"
echo "==> 4/4 校验行数"
for t in admin_users users ssh_keys approvals audit_logs sessions settings mail_logs otp_codes password_reset_tokens; do
s=$(sqlite3 "$SQLITE_DB" "SELECT count(*) FROM $t;" 2>/dev/null || echo "?")
m=$(mysql --protocol=tcp --batch --skip-column-names "$MYSQL_DSN" -e "SELECT count(*) FROM \`$t\`;" 2>/dev/null || echo "?")
printf " %-24s sqlite=%-6s mysql=%s\n" "$t" "$s" "$m"
done
echo
echo "迁移完成。建议:"
echo " 1. 抽查关键表(users/sessions)内容与时间字段格式"
echo " 2. 用指向 MySQL 的配置启动 usernode serve 验证"
echo " 3. 确认无误后再处理旧 SQLite 库"