Skip to content

spec: session continuity across non-atomic batch statements is undefined -- make the non-guarantee normative #26

Description

@ssilvius

Origin

smugglr, bp 019fb06d-6e38 / signal 019fb06d-fa4c (2026-07-30): S6.2.1's MUST fixes execution ORDER for non-atomic batches but says nothing about whether the statements share a CONNECTION. A server that runs them one at a time, in array order, each on a fresh pooled connection, conforms to every word of 6.2.1. The consumer pattern at risk is statement-0-as-precondition: connection-scoped-but-not-transaction-scoped state that survives autocommit and dies only on a connection swap.

The two unconfounded cases from the report:

  • PRAGMA foreign_keys = ON as statement 0. Survives autocommit (proven incidentally by smugglr's FK test); on a pooled server it reverts to SQLite's default OFF mid-batch and FK enforcement silently vanishes. No error in either direction.
  • CREATE TEMP TABLE as statement 0, read by later statements. Becomes "no such table" on a swap.

The ask: is session continuity across the statements of a non-atomic batch guaranteed, denied, or undefined? Silence is the only answer a consumer cannot build on.

Ruling: not guaranteed, and the spec must say so

Session continuity across the statements of a non-atomic batch is NOT guaranteed. The spec should state the non-guarantee normatively rather than leave it to silence. Grounds:

  1. The D1 reference server is the existence proof. Its non-atomic path is a loop of independent db.prepare().all() calls (examples/cloudflare-worker-to-d1/src/index.ts:99-108). Each statement is its own round trip to D1, and the D1 binding offers no session pinning. A continuity guarantee would make a reference implementation non-conforming on day one -- the same trap class the statementIndex MUST scoping (spec: define non-atomic batch failure behavior #9) was designed around.
  2. Stateless, edge-friendly is the spec's identity. Many target backends (D1, HTTP-based SQL services) have no connection concept the server could pin even if it wanted to. Guaranteeing affinity excludes exactly the deployment class this spec exists for.
  3. Transport, not policy (S10.1). A session is the beginning of server-side state between statements; granting one changes what the protocol is.

"Undefined" is rejected too: undefined is silence with a label. The buildable form is an explicit client-side MUST NOT plus a server-side MAY.

Proposed spec text (S6.2.1 addition)

http-sql defines no session. Servers MAY execute each statement of a non-atomic batch on a different database connection or session. Clients MUST NOT rely on connection-scoped state -- temporary tables, session-scoped PRAGMAs or variables, deferred-constraint modes -- established by one statement of a non-atomic batch being visible to any later statement. Each statement of a non-atomic batch MUST be self-contained.

Non-normative note to accompany it:

An atomic batch executes inside a single transaction (section 4.2), and a transaction is bound to a single connection in every known engine, so connection-scoped state established inside an atomic batch survives for the batch's duration in practice. That is a consequence of the transaction obligation, not a session grant: http-sql still defines no session semantics. A client that needs cross-statement session state should send atomic: true or restructure its statements to be self-contained.

Client conformance (S10.2) gains: treat each statement of a non-atomic batch as self-contained (section 6.2.1).

Conformance

No black-box case can verify the absence of a guarantee, and under this ruling a connection-pooling server that passes B-3/B-4/B-5 is conforming, not a gap. conformance/README.md gets a prose note: the B cases do not probe session continuity, and neither servers nor clients may infer a continuity guarantee from a passing run.

Versioning

Additive clarification of previously-undefined behavior; rides the next minor wave alongside #25.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions