Skip to content

Add a SQL Statement Execution fake to the testserver#5432

Merged
pietern merged 8 commits into
mainfrom
sqlexec-iterate
Jun 4, 2026
Merged

Add a SQL Statement Execution fake to the testserver#5432
pietern merged 8 commits into
mainfrom
sqlexec-iterate

Conversation

@pietern

@pietern pietern commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds libs/testserver/testsql, a pluggable fake of the SQL Statement Execution API, so tests can drive /api/2.0/sql/statements natively instead of hand-building StatementResponse stubs.

Tests register matchers via Server.HandleSQL (exact) / Server.HandleSQLPattern (regex), each mapping a statement to a declarative result — columns, rows, an optional error, a poll count, and a chunk count. The testserver models the full lifecycle over the real HTTP endpoints: submit (honoring wait_timeout), poll, chunk pagination, and cancel. A matcher runs once per submission, so a matcher that closes over a map can model stateful resources (create then read back).

The lifecycle routes are registered in AddDefaultHandlers as overridable defaults: a raw Server.Handle for the same pattern registered before AddDefaultHandlers wins, as do test.toml stubs in acceptance — preserving an escape hatch for responses the fake doesn't model (malformed bodies, transport errors, custom status codes).

The libs/sqlexec HTTP tests are migrated onto the fake, replacing per-test StatementResponse construction with one-line matchers.

Cross-check against the real API

Behaviors were verified against a live SQL warehouse, which drove three refinements:

  • A no-row statement (0-row SELECT or no-result-set DDL) reports total_chunk_count: 0 with an empty result; the fake now matches (previously it always emitted one empty chunk).
  • status.sql_state is populated on failures (e.g. 42P01); added an assertion to the failed-statement integration test so the typed StatementError.SQLState contract is covered.
  • Documented on Result.Rows that SQL NULL and empty string are indistinguishable there: the wire format encodes NULL as JSON null, but the SDK models cells as [][]string, so null decodes to "". Recovering the distinction would need a null-aware cell type or the ARROW_STREAM format; no caller needs it today.

Test plan

  • sqlexec integration suite passes live (TestSQLExec*, incl. the new SQLState assertion).

This pull request and its description were written by Isaac.

pietern added 5 commits June 4, 2026 08:42
Introduce libs/testserver/testsql, a pluggable fake of the SQL Statement
Execution API. Tests register matchers via Server.HandleSQL / HandleSQLPattern
that map a statement to a declarative result (columns, rows, optional error,
poll count, chunk count); the testserver models the full lifecycle (submit,
poll, chunk pagination, cancel) over the real HTTP endpoints.

Migrate libs/sqlexec's HTTP tests onto this fake so they no longer hand-build
StatementResponse stubs per test.

Co-authored-by: Isaac
A live cross-check against the real Statement Execution API showed that a
statement returning no rows (a 0-row SELECT or a no-result-set DDL) responds
with total_chunk_count=0 and an empty result, not a single empty chunk. Match
that: splitChunks yields zero chunks for empty input and the terminal response
inlines data only when a chunk exists.

Co-authored-by: Isaac
Document on Result.Rows that a SQL NULL and an empty string are
indistinguishable: the API wire format encodes NULL as JSON null, but the SDK
models result cells as [][]string, so JSON null decodes to "". Recovering the
distinction would require a null-aware cell type or the ARROW_STREAM format,
which no caller needs today.

Co-authored-by: Isaac
The Statement Execution API populates status.sql_state (SQLSTATE) on failures
(e.g. 42P01 for a missing relation), confirmed against a live warehouse. Assert
it survives into StatementError so the contract is covered.

Co-authored-by: Isaac
Move the SQL Statement Execution lifecycle routes from testserver.New into
AddDefaultHandlers. Registering them in New made them permanent first-wins
registrations, so tests could not install raw handlers for those paths (needed
for malformed bodies, transport errors, or custom status codes the testsql fake
does not model). As default handlers they are overridable: a raw Server.Handle
for the same pattern registered before AddDefaultHandlers wins, as do test.toml
stubs in acceptance. The libs/sqlexec HTTP tests opt in via a newServer helper.

Co-authored-by: Isaac
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:10 — with GitHub Actions Inactive
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:10 — with GitHub Actions Inactive
@pietern pietern requested a review from simonfaltum June 4, 2026 09:11

@denik denik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it going to be integrated into acceptance testrunner & config? A selftest would be nice to see what it can do.

Add an HTTP test that registers a regex matcher and echoes back a captured
submatch, exercising Server.HandleSQLPattern and Request.Match end-to-end. This
was the only SQL-exec path without coverage; it also gives HandleSQLPattern a
caller (the deadcode check flagged the Server-level wrapper as unreachable).

Co-authored-by: Isaac
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:19 — with GitHub Actions Inactive
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:19 — with GitHub Actions Inactive
Revert the sqlexec HTTP regex test (b48cd77): the regex matcher lives in the
testsql engine and is covered there (TestPatternDispatchSubmatches etc.), so
exercising it through an sqlexec round-trip tested the wrong layer. The only
reason that test existed was to give Server.HandleSQLPattern a caller; without
it the wrapper is unreachable, so remove it. The engine's HandlePattern stays;
a Server-level wrapper returns with its first real consumer (metric views).

Co-authored-by: Isaac
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:23 — with GitHub Actions Inactive
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:23 — with GitHub Actions Inactive
Bring back Server.HandleSQLPattern (removed in the previous commit) and cover it
together with HandleSQL via local testserver tests that drive the POST handler
directly. This keeps the regex matcher in the public testserver API and tested
on the testserver side, rather than through an sqlexec round-trip.

Co-authored-by: Isaac
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:29 — with GitHub Actions Inactive
@pietern pietern temporarily deployed to test-trigger-is June 4, 2026 09:29 — with GitHub Actions Inactive
@pietern pietern enabled auto-merge June 4, 2026 09:46
@pietern

pietern commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

@denik Yes, that's the idea eventually.

@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Commit: 3eef5e7

Run: 26943288950

@pietern pietern added this pull request to the merge queue Jun 4, 2026
Merged via the queue into main with commit e92f999 Jun 4, 2026
28 checks passed
@pietern pietern deleted the sqlexec-iterate branch June 4, 2026 10:26
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Commit: e92f999

Run: 26946120355

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.

3 participants