refactor: 移除对 MySQL 的支持,更新文档和配置以仅支持 SQLite 和 PostgreSQL

This commit is contained in:
Windpicker-owo
2025-12-04 23:30:43 +08:00
parent 63cb81aab6
commit 06a45b3639
18 changed files with 54 additions and 247 deletions

View File

@@ -2,14 +2,12 @@
"""数据库迁移脚本
支持在不同数据库之间迁移数据:
- SQLite <-> MySQL
- SQLite <-> PostgreSQL
- MySQL <-> PostgreSQL
使用方法:
python scripts/migrate_database.py --help
python scripts/migrate_database.py --source sqlite --target postgresql
python scripts/migrate_database.py --source mysql --target postgresql --batch-size 5000
python scripts/migrate_database.py --source postgresql --target sqlite --batch-size 5000
# 交互式向导模式(推荐)
python scripts/migrate_database.py
@@ -25,7 +23,7 @@
实现细节:
- 使用 SQLAlchemy 进行数据库连接和元数据管理
- 采用流式迁移,避免一次性加载过多数据
- 支持 SQLite、MySQL、PostgreSQL 之间的互相迁移
- 支持 SQLite、PostgreSQL 之间的互相迁移
- 批量插入失败时自动降级为逐行插入,最大程度保留数据
"""
@@ -124,7 +122,7 @@ def get_database_config_from_toml(db_type: str) -> dict | None:
"""从 bot_config.toml 中读取数据库配置
Args:
db_type: 数据库类型,支持 "sqlite""mysql""postgresql"
db_type: 数据库类型,支持 "sqlite""postgresql"
Returns:
dict: 数据库配置字典,如果对应配置不存在则返回 None
@@ -148,28 +146,6 @@ def get_database_config_from_toml(db_type: str) -> dict | None:
sqlite_path = os.path.join(PROJECT_ROOT, sqlite_path)
return {"path": sqlite_path}
elif db_type == "mysql":
return {
"host": db_config.get("mysql_host")
or config_data.get("mysql_host")
or "localhost",
"port": db_config.get("mysql_port")
or config_data.get("mysql_port")
or 3306,
"database": db_config.get("mysql_database")
or config_data.get("mysql_database")
or "maibot",
"user": db_config.get("mysql_user")
or config_data.get("mysql_user")
or "root",
"password": db_config.get("mysql_password")
or config_data.get("mysql_password")
or "",
"charset": db_config.get("mysql_charset")
or config_data.get("mysql_charset")
or "utf8mb4",
}
elif db_type == "postgresql":
return {
"host": db_config.get("postgresql_host")
@@ -257,7 +233,7 @@ def create_engine_by_type(db_type: str, config: dict) -> Engine:
"""根据数据库类型创建对应的 SQLAlchemy Engine
Args:
db_type: 数据库类型,支持 sqlite/mysql/postgresql
db_type: 数据库类型,支持 sqlite/postgresql
config: 配置字典
Returns:
@@ -266,15 +242,6 @@ def create_engine_by_type(db_type: str, config: dict) -> Engine:
db_type = db_type.lower()
if db_type == "sqlite":
return create_sqlite_engine(config["path"])
elif db_type == "mysql":
return create_mysql_engine(
host=config["host"],
port=config["port"],
database=config["database"],
user=config["user"],
password=config["password"],
charset=config.get("charset", "utf8mb4"),
)
elif db_type == "postgresql":
return create_postgresql_engine(
host=config["host"],
@@ -512,7 +479,7 @@ def migrate_table_data(
source_table: 源表对象
target_table: 目标表对象
batch_size: 每批次处理大小
target_dialect: 目标数据库方言 (sqlite/mysql/postgresql)
target_dialect: 目标数据库方言 (sqlite/postgresql)
row_limit: 最大迁移行数限制None 表示不限制
Returns:
@@ -738,7 +705,7 @@ class DatabaseMigrator:
def _validate_database_types(self):
"""验证数据库类型"""
supported_types = {"sqlite", "mysql", "postgresql"}
supported_types = {"sqlite", "postgresql"}
if self.source_type not in supported_types:
raise ValueError(f"不支持的源数据库类型: {self.source_type}")
if self.target_type not in supported_types:
@@ -995,7 +962,7 @@ class DatabaseMigrator:
def parse_args():
"""解析命令行参数"""
parser = argparse.ArgumentParser(
description="数据库迁移工具 - 在 SQLite、MySQL、PostgreSQL 之间迁移数据",
description="数据库迁移工具 - 在 SQLite、PostgreSQL 之间迁移数据",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""示例:
# 从 SQLite 迁移到 PostgreSQL
@@ -1008,15 +975,16 @@ def parse_args():
--target-user postgres \
--target-password your_password
# 从 SQLite 迁移到 MySQL
# 从 PostgreSQL 迁移到 SQLite
python scripts/migrate_database.py \
--source sqlite \
--target mysql \
--target-host localhost \
--target-port 3306 \
--target-database maibot \
--target-user root \
--target-password your_password
--source postgresql \
--source-host localhost \
--source-port 5432 \
--source-database maibot \
--source-user postgres \
--source-password your_password \
--target sqlite \
--target-path data/MaiBot_backup.db
# 使用交互式向导模式(推荐)
python scripts/migrate_database.py
@@ -1028,13 +996,13 @@ def parse_args():
parser.add_argument(
"--source",
type=str,
choices=["sqlite", "mysql", "postgresql"],
choices=["sqlite", "postgresql"],
help="源数据库类型(不指定时,在交互模式中选择)",
)
parser.add_argument(
"--target",
type=str,
choices=["sqlite", "mysql", "postgresql"],
choices=["sqlite", "postgresql"],
help="目标数据库类型(不指定时,在交互模式中选择)",
)
parser.add_argument(
@@ -1053,8 +1021,8 @@ def parse_args():
# 源数据库参数(可选,默认从 bot_config.toml 读取)
source_group = parser.add_argument_group("源数据库配置(可选,默认从 bot_config.toml 读取)")
source_group.add_argument("--source-path", type=str, help="SQLite 数据库路径")
source_group.add_argument("--source-host", type=str, help="MySQL/PostgreSQL 主机")
source_group.add_argument("--source-port", type=int, help="MySQL/PostgreSQL 端口")
source_group.add_argument("--source-host", type=str, help="PostgreSQL 主机")
source_group.add_argument("--source-port", type=int, help="PostgreSQL 端口")
source_group.add_argument("--source-database", type=str, help="数据库名")
source_group.add_argument("--source-user", type=str, help="用户名")
source_group.add_argument("--source-password", type=str, help="密码")
@@ -1062,13 +1030,12 @@ def parse_args():
# 目标数据库参数
target_group = parser.add_argument_group("目标数据库配置")
target_group.add_argument("--target-path", type=str, help="SQLite 数据库路径")
target_group.add_argument("--target-host", type=str, help="MySQL/PostgreSQL 主机")
target_group.add_argument("--target-port", type=int, help="MySQL/PostgreSQL 端口")
target_group.add_argument("--target-host", type=str, help="PostgreSQL 主机")
target_group.add_argument("--target-port", type=int, help="PostgreSQL 端口")
target_group.add_argument("--target-database", type=str, help="数据库名")
target_group.add_argument("--target-user", type=str, help="用户名")
target_group.add_argument("--target-password", type=str, help="密码")
target_group.add_argument("--target-schema", type=str, default="public", help="PostgreSQL schema")
target_group.add_argument("--target-charset", type=str, default="utf8mb4", help="MySQL 字符集")
# 跳过表参数
parser.add_argument(
@@ -1113,24 +1080,20 @@ def build_config_from_args(args, prefix: str, db_type: str) -> dict | None:
return {"path": path}
return None
elif db_type in ("mysql", "postgresql"):
elif db_type == "postgresql":
host = getattr(args, f"{prefix}_host", None)
if not host:
return None
config = {
"host": host,
"port": getattr(args, f"{prefix}_port") or (3306 if db_type == "mysql" else 5432),
"port": getattr(args, f"{prefix}_port") or 5432,
"database": getattr(args, f"{prefix}_database") or "maibot",
"user": getattr(args, f"{prefix}_user") or ("root" if db_type == "mysql" else "postgres"),
"user": getattr(args, f"{prefix}_user") or "postgres",
"password": getattr(args, f"{prefix}_password") or "",
"schema": getattr(args, f"{prefix}_schema", "public"),
}
if db_type == "mysql":
config["charset"] = getattr(args, f"{prefix}_charset", "utf8mb4")
elif db_type == "postgresql":
config["schema"] = getattr(args, f"{prefix}_schema", "public")
return config
return None
@@ -1201,14 +1164,14 @@ def interactive_setup() -> dict:
print("只需回答几个问题,我会帮你构造迁移配置。")
print("=" * 60)
db_types = ["sqlite", "mysql", "postgresql"]
db_types = ["sqlite", "postgresql"]
# 选择源数据库
source_type = _ask_choice("请选择【源数据库类型】:", db_types, default_index=0)
# 选择目标数据库(不能与源相同)
while True:
default_idx = 2 if len(db_types) >= 3 else 0
default_idx = 1 if len(db_types) >= 2 else 0
target_type = _ask_choice("请选择【目标数据库类型】:", db_types, default_index=default_idx)
if target_type != source_type:
break
@@ -1231,8 +1194,8 @@ def interactive_setup() -> dict:
source_path = _ask_str("源 SQLite 文件路径", default="data/MaiBot.db")
source_config = {"path": source_path}
else:
port_default = 3306 if source_type == "mysql" else 5432
user_default = "root" if source_type == "mysql" else "postgres"
port_default = 5432
user_default = "postgres"
host = _ask_str("源数据库 host", default="localhost")
port = _ask_int("源数据库 port", default=port_default)
database = _ask_str("源数据库名", default="maibot")
@@ -1245,9 +1208,7 @@ def interactive_setup() -> dict:
"user": user,
"password": password,
}
if source_type == "mysql":
source_config["charset"] = _ask_str("源数据库字符集", default="utf8mb4")
elif source_type == "postgresql":
if source_type == "postgresql":
source_config["schema"] = _ask_str("源数据库 schema", default="public")
# 目标数据库配置(必须显式确认)
@@ -1260,8 +1221,8 @@ def interactive_setup() -> dict:
)
target_config = {"path": target_path}
else:
port_default = 3306 if target_type == "mysql" else 5432
user_default = "root" if target_type == "mysql" else "postgres"
port_default = 5432
user_default = "postgres"
host = _ask_str("目标数据库 host", default="localhost")
port = _ask_int("目标数据库 port", default=port_default)
database = _ask_str("目标数据库名", default="maibot")
@@ -1275,9 +1236,7 @@ def interactive_setup() -> dict:
"user": user,
"password": password,
}
if target_type == "mysql":
target_config["charset"] = _ask_str("目标数据库字符集", default="utf8mb4")
elif target_type == "postgresql":
if target_type == "postgresql":
target_config["schema"] = _ask_str("目标数据库 schema", default="public")
print()