fix: transplant PG init backfill rollback scope + audit-loop state upsert registration (code-to10 v2, from 854f810)
- db: translate INSERT OR IGNORE ... SELECT to ON CONFLICT DO NOTHING; register task_audit_loop_state (task_id) in _OR_REPLACE_PK (direct port). - server: _backfill_default_quotas uses SAVEPOINT containment, no longer commits the caller's transaction (direct port; callers are init_db only). - worker: NOT ported — integration _coord_child_rows has no column-fallback retry loop, so the InFailedSqlTransaction cascade site does not exist here. - tests: rewritten for the integration base (no cloud.audit_loop / _coord_audit_loop_merge here); real-PG cases skip without DSN/driver. (cherry picked from commit 79e6843610fcd275af874e7b4afc57af7ffa579e)
This commit is contained in:
1 parent
741eb387ed
commit
2dc35bd331
3 files changed
+341
-4
No files matched your search
+33
-1
@@ -325,7 +325,9 @@ def _load_postgres_driver():
|
||||
# (语义弱于 REPLACE,调用点只有 project_members 与
|
||||
# slack_users 两张已知主键表,不会走到退化分支)。
|
||||
# - ``INSERT OR IGNORE`` -> ``ON CONFLICT DO NOTHING``:无冲突
|
||||
# 目标,任何唯一冲突都跳过,不改已有行。
|
||||
# 目标,任何唯一冲突都跳过,不改已有行;VALUES 形与
|
||||
# SELECT 形两种写法都翻译(SELECT 形漏翻曾致新鲜 PG 库
|
||||
# init 失败,见 _OR_IGNORE_SELECT_RE)。
|
||||
# - ``PRAGMA table_info(<表>)`` -> information_schema 查询,
|
||||
# 返回 (cid, name, type, notnull, dflt_value, pk) 六元组,
|
||||
# 与 sqlite 的 PRAGMA table_info 行形状一致,调用方取 row[1]
|
||||
@@ -423,6 +425,13 @@ _OR_REPLACE_PK = {
|
||||
"memory_settings": ("user_id",),
|
||||
"task_memory_flags": ("task_id",),
|
||||
"memory_extraction_log": ("task_id",),
|
||||
# 审计循环状态单行表:主键 task_id。未登记时 Postgres 下
|
||||
# INSERT OR REPLACE 退化为 ON CONFLICT DO NOTHING,已存在
|
||||
# 行的 round / last_decision 等更新静默丢弃(2026-10-31
|
||||
# PG 验证实测:跨进程竞态中包 round=2 而状态表 round 恒
|
||||
# 为 1、轮次耗尽时 last_decision 仍为 pushback;SQLite
|
||||
# 原生 OR REPLACE 测不出来,与自动记忆三张表同款坑)。
|
||||
"task_audit_loop_state": ("task_id",),
|
||||
}
|
||||
|
||||
_IDENTITY_TABLES = frozenset(
|
||||
@@ -437,6 +446,13 @@ _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 OR IGNORE ... SELECT 形(VALUES 形之外的第二种写法,
|
||||
# 如配额回填):PG 没有 OR IGNORE 语法,不翻译会直接报
|
||||
# syntax error at or near "OR"(2026-10-31 新鲜 PG 库复现)。
|
||||
_OR_IGNORE_SELECT_RE = _re.compile(
|
||||
r"^\s*INSERT\s+OR\s+IGNORE\s+INTO\s+(\w+)\s*"
|
||||
r"(\([^)]*\))?\s+SELECT\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(
|
||||
@@ -506,6 +522,22 @@ def _translate_sql(sql):
|
||||
"INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING"
|
||||
% (table, cols, values))
|
||||
return _convert_placeholders(translated), "normal"
|
||||
m = _OR_IGNORE_SELECT_RE.match(stripped)
|
||||
if m:
|
||||
# INSERT OR IGNORE ... SELECT 形 -> INSERT INTO ... SELECT
|
||||
# ... ON CONFLICT DO NOTHING:与 VALUES 形同款语义(任何
|
||||
# 唯一冲突都跳过、不改已有行)。旧版只认 VALUES 形,SELECT
|
||||
# 形原样透传到 PG,直接报 syntax error at or near "OR";
|
||||
# 配额回填就是 SELECT 形,init_db 里它的 except 又把整个
|
||||
# 事务 rollback,migrate 的 ALTER 一并被丢掉(见 server
|
||||
# _backfill_default_quotas 的保存点注释)。
|
||||
table = m.group(1)
|
||||
cols = m.group(2) or ""
|
||||
select_body = m.group(3).rstrip().rstrip(";")
|
||||
translated = (
|
||||
"INSERT INTO %s %s SELECT %s ON CONFLICT DO NOTHING"
|
||||
% (table, cols, select_body))
|
||||
return _convert_placeholders(translated), "normal"
|
||||
translated = _AUTOINCREMENT_RE.sub(
|
||||
"BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY", sql)
|
||||
translated = _convert_placeholders(translated)
|
||||
|
||||
Reference in new issue
Block a user