Skip to content

symfonic.memory.backends.postgres_memory_generation_schema

postgres_memory_generation_schema

The memory erase generation table's DDL, attestation and grants (CORE-199).

postgres_memory_generation reads and advances the rows; this module owns what every read first attests: the table beside memory_nodes, its canonical constraint, and exactly two forward triggers, both ENABLE ALWAYS so session_replication_role=replica cannot skip them. Row security, a rewrite rule or any further trigger on the table -- a BEFORE trigger sorting after the guard could rewrite the row it admitted -- refuses as unguarded, as does an inheritance child or parent (a child's rows are read through the table but written past its triggers), or a table or trigger function not owned by memory_nodes' owner (a migration run as another role, or a superuser, installs objects no reader may trust). An UNLOGGED table refuses too: crash recovery empties it, which would silently reopen every erased scope. The columns, the primary key and every constraint expression must be exactly the DDL's: the descendant range read compares keys byte-wise only under the C collation. Constraint expressions are compared as PostgreSQL 16 deparses them; 16 is the attested server version, and on another a reader may refuse as UNGUARDED.

The row trigger's arguments are the install witness, and the forward function refuses every write to a sealed table (postgres_memory_generation_witness).

bound_witness async

bound_witness(conn: Any, table: str, relation: int, rows: Any) -> tuple[int, int]

(witness, server) of relation, once rows and the name are proved its own.

Source code in src/symfonic/memory/backends/postgres_memory_generation_witness.py
async def bound_witness(conn: Any, table: str, relation: int, rows: Any) -> tuple[int, int]:
    """``(witness, server)`` of ``relation``, once ``rows`` and the name are proved its own."""
    if any(row["tableoid"] != relation for row in rows):
        raise MemoryContractError(REBOUND)
    same, witness, server = await conn.fetchval(_BOUND, table, relation)
    if not same:
        raise MemoryContractError(REBOUND)
    if witness is None:
        raise MemoryContractError(UNWITNESSED)
    return int(witness), int(server)

grant_runtime async

grant_runtime(conn: Any, table: str, role: str) -> None

Owner side, in the caller's transaction; role is a validated identifier.

Source code in src/symfonic/memory/backends/postgres_memory_generation_schema.py
async def grant_runtime(conn: Any, table: str, role: str) -> None:
    """Owner side, in the caller's transaction; ``role`` is a validated identifier."""
    await conn.execute(f'REVOKE ALL ON {table} FROM "{role}"')
    await conn.execute(f'GRANT {", ".join(RUNTIME_GRANTS)} ON {table} TO "{role}"')
    if drift := await runtime_drift(conn, table, role):
        raise MemoryContractError(drift)
    log = table.rsplit(".", 1)[0] + ".memory_erase_log"  # CORE-171: checkouts read, forgets append
    if await conn.fetchval("SELECT to_regclass($1)", log) is not None:
        await conn.execute(f'REVOKE ALL ON {log} FROM "{role}"')
        await conn.execute(f'GRANT SELECT, INSERT ON {log} TO "{role}"')

runtime_drift async

runtime_drift(conn: Any, table: str, role: str) -> str | None

How role departs from read-and-advance on table, or None.

Source code in src/symfonic/memory/backends/postgres_memory_generation_schema.py
async def runtime_drift(conn: Any, table: str, role: str) -> str | None:
    """How ``role`` departs from read-and-advance on ``table``, or None."""
    for privilege in RUNTIME_GRANTS + _RUNTIME_EXCESS:
        wanted = privilege in RUNTIME_GRANTS
        held = await conn.fetchval(HELD, role, table, privilege, not wanted, list(COLUMN_KINDS))
        if held is not wanted:
            return f"{role} {'lacks' if not held else 'holds'} {privilege} on {table}"
    return None