修 GitHub App 安装在 Postgres 上崩:适配层译 INSERT OR IGNORE,安装后增减仓库同步

This commit is contained in:
host committed 2026-10-26 16:11:29 +08:00
1 parent f07b3ccc7c
commit 9fa80bef89
3 files changed
+101 -8

No files matched your search

+25 -3
View File
@@ -310,9 +310,10 @@ def _load_postgres_driver():
# 不重构所有 store 的前提:现有代码到处用 ``?`` 占位符、
# ``row["col"]``、``conn.execute().fetchall()``、``commit`` /
# ``rollback`` / ``close``、``cursor.lastrowid``、``cursor.rowcount``,
# 以及少量 SQLite 方言(``INSERT OR REPLACE``、``PRAGMA
# table_info``、``AUTOINCREMENT``、FTS5 虚拟表)。适配层在
# 执行前做最小翻译,翻译不了的方言明确报错,绝不静默改语义:
# 以及少量 SQLite 方言(``INSERT OR REPLACE``、``INSERT OR
# IGNORE``、``PRAGMA table_info``、``AUTOINCREMENT``、FTS5 虚拟表)。
# 适配层在执行前做最小翻译,翻译不了的方言明确报错,绝不静默
# 改语义:
# - ``?`` -> ``%s``:只转换单引号字符串字面量之外的 ``?``
# (扫描时跟踪单引号与 ``''`` 转义,字面量里的 ``?`` 不动);
# SQL 里已有的 ``%s``(例如调用方自己拼的 ``IN (%s)`` 占位串
@@ -322,6 +323,8 @@ def _load_postgres_driver():
# _OR_REPLACE_PK;未知表退化为 ``ON CONFLICT DO NOTHING``
# (语义弱于 REPLACE,调用点只有 project_members 与
# slack_users 两张已知主键表,不会走到退化分支)。
# - ``INSERT OR IGNORE`` -> ``ON CONFLICT DO NOTHING``:无冲突
# 目标,任何唯一冲突都跳过,不改已有行。
# - ``PRAGMA table_info(<表>)`` -> information_schema 查询,
# 返回 (cid, name, type, notnull, dflt_value, pk) 六元组,
# 与 sqlite 的 PRAGMA table_info 行形状一致,调用方取 row[1]
@@ -411,6 +414,7 @@ def _convert_placeholders(sql):
_OR_REPLACE_PK = {
"project_members": ("project_id", "user_id"),
"slack_users": ("slack_id",),
"github_app_config": ("id",),
}
_IDENTITY_TABLES = frozenset(
@@ -422,6 +426,10 @@ _OR_REPLACE_RE = _re.compile(
r"^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+(\w+)\s*\(([^)]+)\)\s*"
r"VALUES\s*\((.*)\)\s*$",
_re.IGNORECASE | _re.DOTALL)
_OR_IGNORE_RE = _re.compile(
r"^\s*INSERT\s+OR\s+IGNORE\s+INTO\s+(\w+)\s*\(([^)]+)\)\s*"
r"VALUES\s*\((.*)\)\s*$",
_re.IGNORECASE | _re.DOTALL)
_INSERT_TABLE_RE = _re.compile(
r"^\s*INSERT\s+INTO\s+(?:\w+\.)?(\w+)", _re.IGNORECASE)
_PRAGMA_INFO_RE = _re.compile(
@@ -477,6 +485,20 @@ def _translate_sql(sql):
"INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING"
% (table, ", ".join(cols), values))
return _convert_placeholders(translated), "normal"
m = _OR_IGNORE_RE.match(stripped)
if m:
# INSERT OR IGNORE(SQLite 方言)-> ON CONFLICT DO NOTHING:
# 无冲突目标即可,任何唯一冲突都跳过,不改已有行。2026-10-26
# 实弹教训:GitHub App 安装事件在生产 Postgres 上因这句未
# 翻译直接报语法错,安装记录丢了(delivery 已认领,重试会
# 判 duplicate,事件无法靠重发恢复,只能卸载重装)。
table = m.group(1)
cols = m.group(2)
values = m.group(3)
translated = (
"INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING"
% (table, cols, values))
return _convert_placeholders(translated), "normal"
translated = _AUTOINCREMENT_RE.sub(
"BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY", sql)
translated = _convert_placeholders(translated)