Files
ctbjrj/tools/refactor_triggers.py

170 lines
9.8 KiB
Python

# -*- coding: utf-8 -*-
"""触发器生成器重构:①去掉聚合用列引用模式 ②箱号表数量触发器按箱号行生成(WHEN 行ID,真 SUM)
③图号行触发器传箱号表 ④重建/新表入口同步。幂等。"""
import io
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\计算引擎.cs"
s = io.open(P, encoding="utf-8-sig").read()
n = 0
def rep(old, new):
global s, n
if new in s and old not in s:
print("已改(跳过):", old[:50].replace("\n", " "))
return
assert old in s, "锚点缺失: " + old[:80]
s = s.replace(old, new, 1)
n += 1
# 1) 子翻译 透传清理
rep("p.引用模式 = 引用模式; p.下层表 = 下层表; p.层内公式 = 层内公式; p.展开栈 = 展开栈; p.聚合用列引用 = 聚合用列引用;",
"p.引用模式 = 引用模式; p.下层表 = 下层表; p.层内公式 = 层内公式; p.展开栈 = 展开栈;")
# 2) 解析器删字段
rep(''' public string 引用模式 = "NEW"; // NEW=触发器 NEW."列" / ROW=上级目标行 "列"
public bool 聚合用列引用 = false; // true=SUM('列') 直接引用本行列(该聚合由下级触发器维护)''',
''' public string 引用模式 = "NEW"; // NEW=触发器 NEW."列" / ROW=上级目标行 "列"''')
# 3) Sum子查询 删分支
rep(''' /// <summary>SUM('列') → 对下层表整表求和;或(自触发场景)直接引用本行列(由下级触发器维护)。</summary>
private string Sum子查询(string col)
{
string c = "\\"" + col.Replace("\\"", "\\"\\"") + "\\"";
if (聚合用列引用)
return "COALESCE(CAST(" + (引用模式 == "NEW" ? "NEW." : "") + c + " AS REAL),0)";
return "COALESCE((SELECT CAST(SUM(CAST(" + c + " AS REAL)) AS REAL) FROM \\"" + 下层表.Replace("\\"", "\\"\\"") + "\\"),0)";
}''',
''' /// <summary>SUM('列') → 对下层表整表求和(每箱号一张元件表/每图号一张箱号表,无条件)。</summary>
private string Sum子查询(string col)
{
return "COALESCE((SELECT CAST(SUM(CAST(\\"" + col.Replace("\\"", "\\"\\"") +
"\\" AS REAL)) AS REAL) FROM \\"" + 下层表.Replace("\\"", "\\"\\"") + "\\"),0)";
}''')
# 4) 翻译公式 删参数
rep(''' private static string 翻译公式(string 表达式, Dictionary<string, 公式条目> 层内公式,
string 引用模式, string 下层表, out string 错误, bool 聚合用列引用 = false)''',
''' private static string 翻译公式(string 表达式, Dictionary<string, 公式条目> 层内公式,
string 引用模式, string 下层表, out string 错误)''')
rep(''' { 引用模式 = 引用模式, 下层表 = 下层表, 层内公式 = 层内公式, 展开栈 = new HashSet<string>(), 聚合用列引用 = 聚合用列引用 };''',
''' { 引用模式 = 引用模式, 下层表 = 下层表, 层内公式 = 层内公式, 展开栈 = new HashSet<string>() };''')
# 5) 生成Set片段 删参数
rep("private static string 生成Set片段(List<公式条目> 公式, string 引用模式, string 下层表, bool 聚合用列引用 = false)",
"private static string 生成Set片段(List<公式条目> 公式, string 引用模式, string 下层表)")
rep("string sql = 翻译公式(f.表达式, map, 引用模式, 下层表, out err, 聚合用列引用);",
"string sql = 翻译公式(f.表达式, map, 引用模式, 下层表, out err);")
rep("{ string e2; sql = 翻译公式(d[2], map, 引用模式, 下层表, out e2, 聚合用列引用); break; }",
"{ string e2; sql = 翻译公式(d[2], map, 引用模式, 下层表, out e2); break; }")
# 6) 建箱号表触发器:按行生成
rep(''' /// <summary>为一张箱号表生成触发器(u/i/d):行内公式 + 所属图号行聚合。</summary>
public static void 建箱号表触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
string 箱号表, string 图号ID, string 图号表名,
List<公式条目> 箱号公式, List<公式条目> 图号公式)
{
Drop触发器(conn, tx, 箱号表);
string of = "";
foreach (var c in 箱号输入列) of += "\\"" + c + "\\",";
of = of.TrimEnd(',');
string xhSetNew = 生成Set片段(箱号公式, "NEW", null, true); // 本行(数量变);单台成本等由元件触发器维护
string xhSetRow = 生成Set片段(箱号公式, "ROW", 箱号表, true); // 同上
string thSet = 生成Set片段(图号公式, "ROW", 箱号表);
string 刷图号 = "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';";
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
箱号表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
箱号表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetRow +
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
}''',
''' /// <summary>
/// 为一张箱号表生成触发器。箱号聚合依赖"每行各自的元件表",数量更新触发器按行生成
/// (WHEN NEW."内部ID"=行ID,体内 SUM 子查询 FROM 该行的元件表);INSERT/DELETE 表级只刷图号聚合。
/// 行列表:每项 [箱号ID, 该行元件表名]。
/// </summary>
public static void 建箱号表触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
string 箱号表, string 图号ID, string 图号表名, List<string[]> 行列表,
List<公式条目> 箱号公式, List<公式条目> 图号公式)
{
Drop触发器(conn, tx, 箱号表);
string of = "";
foreach (var c in 箱号输入列) of += "\\"" + c + "\\",";
of = of.TrimEnd(',');
string thSet = 生成Set片段(图号公式, "ROW", 箱号表);
string 刷图号 = "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';";
foreach (var 行 in 行列表)
{
string 行ID = 行[0], yj表 = 行[1];
if (string.IsNullOrEmpty(yj表)) continue;
string xhSetNew = 生成Set片段(箱号公式, "NEW", yj表);
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(行ID) +
"_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
行ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
}
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
}''')
# 7) 建图号行触发器:真 SUM
rep(''' public static void 建图号行触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
string 图号表名, string 图号ID, List<公式条目> 图号公式)
{
string thSetNew = 生成Set片段(图号公式, "NEW", null, true);''',
''' public static void 建图号行触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
string 图号表名, string 图号ID, string 箱号表, List<公式条目> 图号公式)
{
string thSetNew = 生成Set片段(图号公式, "NEW", 箱号表);''')
# 8) 重建项目全部触发器:组装行列表
rep(''' // 先建箱号表映射后建元件触发器(元件触发器引用箱号表名,顺序无碍;图号行触发器每图号一次)
var 已建图号 = new HashSet<string>();
foreach (var m in 箱号行列表)
建箱号表触发器(conn, tx, m[0], m[1], 图号表名, 箱号公式, 图号公式);
foreach (var m in 元件表列表)
建元件表触发器(conn, tx, m[0], m[1], m[2], m[3], 图号表名, 元件公式, 箱号公式, 图号公式);
foreach (var kv in 箱号表Of图号)
if (已建图号.Add(kv.Key))
建图号行触发器(conn, tx, 图号表名, kv.Key, 图号公式);''',
''' // 按箱号表分组元件表:[箱号表] → [箱号ID, 元件表] 列表
var 行按表 = new Dictionary<string, List<string[]>>();
foreach (var m in 元件表列表)
{
List<string[]> rows;
if (!行按表.TryGetValue(m[1], out rows)) { rows = new List<string[]>(); 行按表[m[1]] = rows; }
rows.Add(new[] { m[2], m[0] });
}
var 已建图号 = new HashSet<string>();
foreach (var m in 箱号行列表)
{
List<string[]> rows;
if (!行按表.TryGetValue(m[0], out rows)) rows = new List<string[]>();
建箱号表触发器(conn, tx, m[0], m[1], 图号表名, rows, 箱号公式, 图号公式);
}
foreach (var m in 元件表列表)
建元件表触发器(conn, tx, m[0], m[1], m[2], m[3], 图号表名, 元件公式, 箱号公式, 图号公式);
foreach (var kv in 箱号表Of图号)
if (已建图号.Add(kv.Key))
建图号行触发器(conn, tx, 图号表名, kv.Key, kv.Value, 图号公式);''')
# 9) 建新表触发器:箱号分支传空行列表
rep(''' else if (新表类型 == "箱号")
建箱号表触发器(conn, tx, 箱号表, 图号ID, 图号表名, 箱号公式, 图号公式);''',
''' else if (新表类型 == "箱号")
{
// 新箱号表暂无行级元件表映射;行级触发器由"建元件表触发器时同批重建/进项目自检"补齐
建箱号表触发器(conn, tx, 箱号表, 图号ID, 图号表名, new List<string[]>(), 箱号公式, 图号公式);
}''')
io.open(P, "w", encoding="utf-8-sig").write(s)
print("重构完成", n, "处")