Skip to content

exec() refuses SQL without a statement; new events in the runtime's async context - #7

Merged
mkotelnikov merged 3 commits into
mainfrom
ccr-ab3c0d30-6du8do-small-gaps
Oct 27, 2026
Merged

mkotelnikov merged 3 commits into
mainfrom
ccr-ab3c0d30-6du8do-small-gaps

Conversation

@mkotelnikov

@mkotelnikov mkotelnikov commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Updated after main (48f6222) brought its own sql.ingest(). This PR's ingest, and its workerd-style error wording, gave way to main's, which follows celld and is checked against celld's recording (sql-ingest). What remains:

1. sql.exec() refuses SQL without a statement

  • SQL with no statement ("", ";"), or whose last "statement" is only comments (SELECT 1; -- note), now throws SQL code did not contain a statement., as workerd and celld do. cellb used to run it, or failed with "statement has been finalized".
  • The check is lacksStatement in sql-split.ts, built on main's completeSqlStatements.
  • With it, wd-sql-default stops at the same assertion as celld (ingest's syntax error). Its known gap now names only the remaining difference: celld's report carries its own stack frames.
  • This closes the gap main's docs/gaps.md listed for wd-sql-default.

2. A call to a Durable Object is a new event

On Cloudflare, a called object doesn't see its caller's AsyncLocalStorage stores. cellb now matches: every event delivered to an object (ObjectHost.deliver), and the constructor, starts in the async context the runtime was made in (core/async-context.ts).

  • Node: AsyncLocalStorage.snapshot(), reached through process.getBuiltinModule, so core still imports nothing from Node. Each event also gets a fresh AsyncResource created inside the snapshot. Before Node's AsyncContextFrame, als.run() writes the store onto the current resource, so without this an als.run() in one event leaked into the next event started inside it.
  • Browser: the node:async_hooks polyfill's frame.
  • Runtime-wide stores still work: a store active when createCellRuntime ran reaches every event, which is how p09 attributes each scenario's logs.

3. The failed interop check

It was "Queues: a delayed message sent on cellb is delivered by celld", a timing flake, also fixed in #9.

Tests

  • test/p15-sql-ingest.test.ts:
    • exec() refusals for "", ";" and a trailing comment;
    • main's ingest remainders, and its celld wording of syntax errors;
    • a dump ingested in chunks of 1 to 81 bytes.
  • test/p16-async-context.test.ts: neither a direct call nor an object-to-object call sees the caller's store; a store active when the runtime was made reaches events and constructors.
  • test/async-context-worker.ts: the callee sees no store, on Node and in Chromium.
  • Results, run locally the way CI runs them, on the merge with main:
    • Biome and typecheck pass;
    • pnpm run interop (celld conformance and interop): 123 passed, 6 expected failures;
    • interop:browser: 4 passed;
    • unit suite: 338 passed;
    • full Chromium suite: 206 passed, 15 skipped;
    • flue-example: 6 passed on Node, 8 passed in the browser.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SXJQA3LbggJHi5qZBpUKe8

@mkotelnikov
mkotelnikov added this pull request to stack #8 October 27, 2026 06:41
@mkotelnikov
mkotelnikov force-pushed the ccr-ab3c0d30-6du8do-small-gaps branch 2 times, most recently from eba8fd6 to 8a6db97 Compare October 27, 2026 07:02
Base automatically changed from ccr-ab3c0d30-6du8do-site-parity to main October 27, 2026 07:07
…ime's async context

sql.ingest(sql) (workerd, experimental): runs the complete statements at
the start of an SQL chunk (quotes, comments and trigger bodies respected)
and returns the rest as `remainder`, with rowsRead, rowsWritten and
statementCount; an SQL dump can be fed through it in chunks of any size.

sql.exec(), as workerd: SQL with no statement (`""`, `";"`) or a last
statement of comments alone throws "SQL code did not contain a statement.";
a syntax error is worded `near "X": syntax error at offset N: SQLITE_ERROR`
(the drivers prepare statements without their `;`, where SQLite says
"incomplete input": reworded as the syntax error at that `;`). No celld
recording depends on the old wording. wd-sql-default now gets past ingest
and stops at workerd's wording of other SQLite errors (SQLITE_MISMATCH,
SQLITE_AUTH), where cellb follows celld: still a known gap, judged by
workerd.

A call to a Durable Object is a new event, as on Cloudflare: events and
constructors start in the async context the runtime was made in, not the
caller's (AsyncLocalStorage.snapshot + a fresh AsyncResource per event on
Node, so a store one event sets does not reach the next; the polyfill's
frame in a browser). A host can still scope a whole runtime (p09 does).

Tests: p15 (workerd's ingest assertions, a dump in chunks of 1–81 bytes
with strings, comments and a trigger), p16 (a direct and an object-to-
object call do not see the caller's store; the runtime's store reaches
events and constructors), the async-context worker's expectation now
Cloudflare's (Node and Chromium).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXJQA3LbggJHi5qZBpUKe8
@mkotelnikov
mkotelnikov force-pushed the ccr-ab3c0d30-6du8do-small-gaps branch from 8a6db97 to 5c9dca9 Compare October 27, 2026 07:07
mkotelnikov pushed a commit that referenced this pull request Oct 27, 2026
"Queues: a delayed message sent on cellb is delivered by celld" failed now
and then in CI (#5, #7). celld wakes cells from its wake index by minute
(wake/entries/<UTC minute>/…): a message due in a later minute than celld
started in is woken at its next scan, up to a minute on, and the test
polled for 20 s. Reproduced every time by sending at :54 of a minute
(delivered 59 s after celld started); cellb writes the same entry celld
writes for its own delayed messages. The test now polls for up to 80 s
(timeout 150 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXJQA3LbggJHi5qZBpUKe8
main (48f6222) brought its own sql.ingest(), worded as celld words it and
checked against celld (conformance project sql-ingest). This PR's ingest,
its workerd wording ("… at offset N: SQLITE_ERROR") and its
takeCompleteStatements give way to it: main's sql-split.ts, sql-storage.ts,
scenarios.ts and gaps.md, then on top:

- exec() refuses SQL with no statement, or a last one of comments alone
  ("SQL code did not contain a statement."), as workerd and celld do
  (lacksStatement, on main's completeSqlStatements). With it, wd-sql-default
  stops at the same assertion as celld; its known gap now names only
  celld's stack frames in the report.
- gaps.md keeps this PR's entry for events starting in the runtime's async
  context.
- p15 expects celld's ingest wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXJQA3LbggJHi5qZBpUKe8
"Queues: a delayed message sent on cellb is delivered by celld" failed now
and then in CI (#5, #7). celld wakes cells from its wake index by minute
(wake/entries/<UTC minute>/…): a message due in a later minute than celld
started in is woken at its next scan, up to a minute on, and the test
polled for 20 s. Reproduced every time by sending at :54 of a minute
(delivered 59 s after celld started); cellb writes the same entry celld
writes for its own delayed messages. The test now polls for up to 80 s
(timeout 150 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXJQA3LbggJHi5qZBpUKe8
@mkotelnikov mkotelnikov changed the title sql.ingest(), workerd's SQL error wording, and new events in the runtime's async context exec() refuses SQL without a statement; new events in the runtime's async context Oct 27, 2026
@mkotelnikov
mkotelnikov merged commit e38c19d into main Oct 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants