pbl_runtime_ext/.rtx5tmp/patch_rtx0005.py
2026-09-22 13:56:48 +08:00

166 lines
6.6 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 -*-
"""PBL-RTX-0005 生成器补丁:修 pbl_assert_append_only 过程体 + _with_delimiter 行尾注释加固。
每处替换都断言命中,未命中即 rc=1(禁止静默漏改)。
"""
import io
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
MODULE_DIR = os.path.dirname(HERE)
TARGET = os.path.join(MODULE_DIR, "scripts", "pbl_runtime_event_ddl.py")
src = io.open(TARGET, encoding="utf-8").read()
orig = src
applied = []
def rep(old, new, tag):
global src
n = src.count(old)
if n != 1:
print("FAIL 定位不唯一(%d):%s" % (n, tag))
sys.exit(1)
src = src.replace(old, new)
applied.append(tag)
# ---------------------------------------------------------------- 1) 过程体 RETURN; → 正向条件 SIGNAL
rep(
"""CREATE PROCEDURE `pbl_assert_append_only`(
IN p_table VARCHAR(64), IN p_priv VARCHAR(16), IN p_detail VARCHAR(255))
READS SQL DATA
BEGIN
IF p_table <> '%(table)s' THEN
RETURN; -- 只守护事件表,其他表不受影响
END IF;
IF IFNULL(@pbl_guard_ctx, '') = '%(ctx)s' THEN
RETURN; -- break-glass:一次性历史数据回填
END IF;
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'PBL_APPEND_ONLY_DENIED',
MYSQL_ERRNO = 45001;
END;""",
"""CREATE PROCEDURE `pbl_assert_append_only`(
IN p_table VARCHAR(64), IN p_priv VARCHAR(16), IN p_detail VARCHAR(255))
READS SQL DATA
BEGIN
-- PBL-RTX-0005:MariaDB 存储过程体禁用 RETURN(RETURN 只在 FUNCTION 中合法),
-- 旧写法 `IF ... THEN RETURN; END IF;` 在建过程阶段即 ERROR 1064,导致本过程
-- 连同其后的触发器/维护过程全部未创建。改为等价正向条件单语句 SIGNAL:
-- 仅「是本表」且「非 break-glass 上下文」时才拒绝,其余情形不执行任何语句,
-- 与旧的两段提前返回语义完全一致。
IF p_table = '%(table)s'
AND IFNULL(@pbl_guard_ctx, '') <> '%(ctx)s' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'PBL_APPEND_ONLY_DENIED',
MYSQL_ERRNO = 45001;
END IF;
END;""",
"append_only_sql: RETURN; -> 正向条件 SIGNAL",
)
# ---------------------------------------------------------------- 2) _with_delimiter 行尾注释加固
rep(
'''_BARE_TOKEN = re.compile(r"^(CREATE|DROP|ALTER|GRANT|REVOKE)\\s*\\$\\$\\s*$", re.I)''',
'''_BARE_TOKEN = re.compile(r"^(CREATE|DROP|ALTER|GRANT|REVOKE)\\s*\\$\\$\\s*$", re.I)
# 语句末行的「代码 + 行尾注释」形态:`$$` 若与 `-- 注释` 同行,会被行注释吞掉,
# 客户端永远等不到语句结束符(PBL-RTX-0005 加固项:注释上移,`$$` 独占行尾)。
_TRAILING_COMMENT = re.compile(r"^(?P<code>\\S.*?\\s)(?P<cmt>(?:--|#)\\s.*|/\\*.*\\*/)$")
# 过程/函数/触发器体内非法的裸 RETURN(MariaDB 仅 FUNCTION 允许 RETURN;PROCEDURE 报 1064)
_ILLEGAL_RETURN = re.compile(r"^RETURN\\s*;?\\s*$", re.I)''',
"_with_delimiter: 新增 _TRAILING_COMMENT / _ILLEGAL_RETURN 常量",
)
rep(
""" def flush():
if not buf:
if cmts:
out.append("\\n".join(cmts))
del cmts[:]
return
stmt = re.sub(r";\\s*$", "", "\\n".join(buf).strip())
head = ("\\n".join(cmts) + "\\n") if cmts else ""
out.append(head + stmt + delim)
del buf[:]
del cmts[:]""",
""" def flush():
if not buf:
if cmts:
out.append("\\n".join(cmts))
del cmts[:]
return
lines = list(buf)
# 末行行尾注释上移到语句之前:否则产出 `... -- note$$`,`$$` 落在注释内
# → 客户端认为语句未结束,与下一条语句黏连(同类 1064 根因)。
tail_cmts = []
m = _TRAILING_COMMENT.match(lines[-1].rstrip())
if m and not m.group("code").lstrip().startswith(("--", "#")):
tail_cmts.append(m.group("cmt"))
lines[-1] = m.group("code")
stmt = re.sub(r";\\s*$", "", "\\n".join(lines).strip())
head_lines = cmts + tail_cmts
head = ("\\n".join(head_lines) + "\\n") if head_lines else ""
out.append(head + stmt + delim)
del buf[:]
del cmts[:]""",
"_with_delimiter.flush(): 末行行尾注释上移",
)
# ---------------------------------------------------------------- 3) 静态断言:禁止过程体裸 RETURN
rep(
'''def _delimiter_integrity_errors(text, label=""):''',
'''def _procedure_body_return_errors(text, label=""):
"""PBL-RTX-0005 回归门禁:`CREATE PROCEDURE` 体内不得出现裸 `RETURN`。
MariaDB/MySQL 的 RETURN 语句只允许出现在 FUNCTION(及触发器外的函数语义)中;
存储过程里写 `RETURN;` 会在 CREATE PROCEDURE 阶段直接 ERROR 1064,且因为整段
在 `DELIMITER $$` 块内,失败会连带后续触发器/维护过程一起未创建(现场表现为
`SHOW PROCEDURE STATUS` 0 个 + CALL 报 1305 does not exist)。
返回缺陷描述列表;空列表 = 通过。
"""
errs = []
pfx = ("[%s] " % label) if label else ""
in_proc = False
for i, ln in enumerate(text.split("\\n"), 1):
s = ln.strip()
if re.match(r"^CREATE\\s+(OR\\s+REPLACE\\s+)?PROCEDURE\\b", s, re.I):
in_proc = True
continue
if in_proc and re.match(r"^CREATE\\s+\\w", s, re.I):
in_proc = False
if in_proc and _ILLEGAL_RETURN.match(s):
errs.append(pfx + "L%d 存储过程体内出现裸 RETURN(MariaDB 过程体非法 → ERROR 1064): %r"
% (i, s))
return errs
def _delimiter_integrity_errors(text, label=""):''',
"新增 _procedure_body_return_errors() 回归门禁函数",
)
rep(
""" struct_errs = []
for label, text in artifacts:
struct_errs.extend(_delimiter_integrity_errors(text, label))""",
""" struct_errs = []
for label, text in artifacts:
struct_errs.extend(_delimiter_integrity_errors(text, label))
# PBL-RTX-0005:过程体裸 RETURN 静态门禁(实库 1064 的根因形态)
struct_errs.extend(_procedure_body_return_errors(text, label))""",
"verify(): 接入过程体 RETURN 静态门禁",
)
if "PBL_APPEND_ONLY_DENIED" not in src or src.count("RETURN;") > 0:
print("FAIL 校验:src 中仍残留 `RETURN;`")
sys.exit(1)
io.open(TARGET, "w", encoding="utf-8").write(src)
print("OK 已应用 %d 处替换:" % len(applied))
for a in applied:
print(" -", a)
print("bytes %d -> %d" % (len(orig), len(src)))