fix(state): skip the display-trigger drop+recreate when the triggers already match

DEFERRED_INDEX_SQL unconditionally DROPs and re-CREATEs the four display
triggers on every open so a changed trigger body rolls out; DROP TRIGGER IF
EXISTS on an existing trigger takes the write lock, so a settled database
still blocked behind a sibling's transaction after the three statement
gates. Compare each trigger's stored sql against the desired CREATE text and
run the pair only when they differ. CREATE ... IF NOT EXISTS forms are
lock-free on an existing object and run as written. The no-writes test now
also traces DROP TRIGGER / ALTER.
This commit is contained in:
kshitijk4poor
2026-09-09 23:41:22 +05:30
committed by Teknium
parent c4c7e102aa
commit ff59fcd710
2 changed files with 37 additions and 2 deletions

View File

@@ -282,6 +282,41 @@ class SessionSchemaMixin:
high_water = cursor.execute("SELECT COALESCE(MAX(id), 0) FROM messages").fetchone()[0]
cursor.execute(_STATE_META_UPSERT_SQL, (FTS_TOOL_FULL_CONTENT_HIGH_WATER_KEY, str(high_water)))
@staticmethod
def _execute_ddl_skipping_settled_triggers(cursor: sqlite3.Cursor, ddl: str) -> None:
"""Run *ddl* statement by statement, skipping each ``DROP TRIGGER IF EXISTS x`` /
``CREATE TRIGGER IF NOT EXISTS x …`` pair whose trigger already exists with the same body.
``DROP TRIGGER IF EXISTS`` on an existing trigger takes SQLite's write lock (the
``CREATE … IF NOT EXISTS`` forms do not), so the unconditional drop+recreate that lets a
changed trigger body roll out would otherwise block every open of a settled database
behind a sibling process's transaction. ``sqlite_master.sql`` stores the CREATE text
verbatim minus ``IF NOT EXISTS`` and the ``;``, so an exact comparison decides.
"""
stored = dict(cursor.execute("SELECT name, sql FROM sqlite_master WHERE type = 'trigger'").fetchall())
pending_drop: Optional[str] = None
statement = ""
for line in ddl.splitlines():
statement += line + "\n"
if not sqlite3.complete_statement(statement):
continue
statement, current = "", statement.strip()
upper = current.upper()
if upper.startswith("DROP TRIGGER IF EXISTS "):
pending_drop = current
continue
if upper.startswith("CREATE TRIGGER IF NOT EXISTS "):
desired = current.replace("IF NOT EXISTS ", "", 1).rstrip(";")
if stored.get(desired.split(None, 3)[2]) == desired:
pending_drop = None
continue
if pending_drop is not None:
cursor.execute(pending_drop)
pending_drop = None
cursor.execute(current)
if statement.strip():
raise sqlite3.OperationalError("incomplete DDL statement")
@staticmethod
def _execute_ddl_script_transactional(cursor: sqlite3.Cursor, ddl: str) -> None:
"""Execute a DDL script without ``executescript``'s implicit commit."""
@@ -884,7 +919,7 @@ class SessionSchemaMixin:
)
except sqlite3.OperationalError as exc:
logger.debug("idx_messages_platform_msg_id create skipped: %s", exc)
cursor.executescript(DEFERRED_INDEX_SQL) # same ordering constraint (``active``)
self._execute_ddl_skipping_settled_triggers(cursor, DEFERRED_INDEX_SQL) # same ordering constraint (``active``)
# Heal NULL ``active`` rows on every startup: older reconciler builds added ``active``
# without NOT NULL DEFAULT 1, so ``WHERE active = 1`` loaders hid whole histories. A

View File

@@ -640,7 +640,7 @@ class TestMessageStorage:
conn = real_connect(*args, **kwargs)
conn.set_trace_callback(
lambda stmt: writes.append(stmt)
if re.match(r"\s*(INSERT|UPDATE|DELETE|REPLACE)\b", stmt, re.I) and "temp." not in stmt
if re.match(r"\s*(INSERT|UPDATE|DELETE|REPLACE|ALTER|DROP\s+TRIGGER)\b", stmt, re.I) and "temp." not in stmt
else None
)
return conn