Skip to content

Add execute() for running raw SQL inside a transaction() block #31

@0x054

Description

@0x054

Ferro's transaction context manager (async with transaction():) gives us guaranteed connection affinity for ORM calls — but there is no public API to run an arbitrary SQL statement on that same connection. This blocks legitimate uses such as setting Postgres GUCs (SET LOCAL, set_config()), advisory locks, LISTEN/NOTIFY setup, or one-off statements that don't fit a Model.

Proposed API

async def execute(sql: str, *args, tx_id: str | None = None) -> None:
    """Run a raw statement on the current transaction's connection.

    Uses positional asyncpg-style placeholders ($1, $2, ...).
    Honors the active `transaction()` context if `tx_id` is omitted.
    """

Acceptance criteria

  • When called inside async with transaction():, runs on that transaction's connection.
  • When called outside any transaction, runs on a one-off pool connection.
  • Parameters bind as $1, $2, ...; reject embedded interpolation.
  • Errors surface as the same exception types Ferro's CRUD calls already raise.

Motivation

A downstream project (Blueberry) is implementing Postgres row-level security and needs to run select set_config('request.jwt.claims', $1, true) per request inside the request's Ferro transaction. This is the only Ferro gap blocking that work; full design at docs/superpowers/specs/2026-04-27-rls-security-design.md in the consumer repo.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions