hr-system/scripts/apply_ddl.py

119 lines
5.3 KiB
Python
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 python3
# -*- coding: utf-8 -*-
"""DDL 迁移基座执行器T01/T02幂等导入 models JSON 生成的 DDL 至 MariaDB hrs 库。
特性:
- CREATE TABLE IF NOT EXISTS可重跑
- 缺索引自动补建(先查 information_schema.STATISTICS
- 迁移台账写入 hrs.schema_migrationversion/表数/时间/校验和),重复执行只记一条最新;
- 依赖 mysql 客户端或 pymysql自动探测。
用法:
python3 scripts/apply_ddl.py [--host 127.0.0.1] [--user test] [--password test123] [--db hrs] [--models models] [--version T01]
"""
import argparse, glob, hashlib, json, os, shutil, subprocess, sys
BASE = os.path.join(os.path.dirname(__file__), "..")
sys.path.insert(0, os.path.join(BASE, "scripts"))
from json2ddl import gen_table_ddl, gen_index_guard_sql # noqa: E402
def run_sql_cli(sql, args):
cmd = ["mysql", f"-h{args.host}", f"-P{args.port}", f"-u{args.user}", f"-p{args.password}", args.db]
p = subprocess.run(cmd, input=sql.encode("utf-8"), capture_output=True)
if p.returncode != 0:
raise RuntimeError(p.stderr.decode("utf-8", "ignore"))
return p.stdout.decode("utf-8", "ignore")
def exec_sql(sql, args):
try:
import pymysql
conn = pymysql.connect(host=args.host, port=args.port, user=args.user,
password=args.password, database=args.db, charset="utf8mb4")
try:
with conn.cursor() as cur:
cur.execute(sql)
conn.commit()
return None
finally:
conn.close()
except ImportError:
run_sql_cli(sql, args)
return None
def query_sql(sql, args):
if shutil.which("mysql"):
out = run_sql_cli(sql + "\n", args)
return [ln.split("\t") for ln in out.strip().splitlines() if ln]
import pymysql
conn = pymysql.connect(host=args.host, port=args.port, user=args.user,
password=args.password, database=args.db, charset="utf8mb4")
try:
with conn.cursor() as cur:
cur.execute(sql)
return [list(r) for r in cur.fetchall()]
finally:
conn.close()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", default=3306, type=int)
ap.add_argument("--user", default="test")
ap.add_argument("--password", default="test123")
ap.add_argument("--db", default="hrs")
ap.add_argument("--models", default=os.path.join(BASE, "models"))
ap.add_argument("--version", default="T01")
args = ap.parse_args()
# 0) 迁移台账表
exec_sql("""CREATE TABLE IF NOT EXISTS `schema_migration` (
`version` VARCHAR(32) NOT NULL COMMENT '迁移版本号',
`applied_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '执行时间',
`table_count` INT NOT NULL DEFAULT 0 COMMENT '本次管理表数',
`checksum` VARCHAR(64) DEFAULT NULL COMMENT 'models JSON 合并校验和',
`operator` VARCHAR(64) DEFAULT 'agent.develop' COMMENT '执行人',
PRIMARY KEY (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='DDL迁移台账(幂等可重跑)';""", args)
# 1) 收集全部 models JSON逐表建表 + 补索引
created, index_added, total = [], [], 0
h = hashlib.sha256()
jobs = []
for moddir in sorted(glob.glob(os.path.join(args.models, "*"))):
if not os.path.isdir(moddir):
continue
for p in sorted(glob.glob(os.path.join(moddir, "*.json"))):
doc = json.load(open(p, encoding="utf-8"))
h.update(open(p, "rb").read())
table, ddl = gen_table_ddl(p)
jobs.append((os.path.basename(moddir), table, ddl, doc))
total += 1
for mod, table, ddl, doc in jobs:
before = query_sql(f"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='{args.db}' AND TABLE_NAME='{table}'", args)
n_exist = int(before[0][0]) if before else 0
exec_sql(ddl, args)
if not n_exist:
created.append(table)
# 索引补建(幂等)
rows = query_sql(f"SELECT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='{args.db}' AND TABLE_NAME='{table}'", args)
existing_idx = {r[0] for r in rows}
for idx in doc.get("indexes", []):
if idx["name"] not in existing_idx:
exec_sql(gen_index_guard_sql(table, idx), args)
index_added.append(f"{table}.{idx['name']}")
checksum = h.hexdigest()[:16]
# 2) 登记迁移台账(幂等 upsert
exec_sql(f"""INSERT INTO schema_migration (version, table_count, checksum)
VALUES ('{args.version}', {total}, '{checksum}')
ON DUPLICATE KEY UPDATE applied_at=CURRENT_TIMESTAMP, table_count={total}, checksum='{checksum}';""", args)
# 3) 校验
rows = query_sql(f"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='{args.db}'", args)
print(f"[apply_ddl] 管理业务表 {total} 张;新建 {len(created)} 张;补索引 {len(index_added)}")
print(f"[apply_ddl] 新建表: {', '.join(created) if created else '(无,全部已存在,幂等重跑)'}")
print(f"[apply_ddl] schema_migration version={args.version} table_count={total} checksum={checksum}")
print(f"[apply_ddl] 库 {args.db} 当前表总数: {rows[0][0]}")
if __name__ == "__main__":
main()