Skip to content

docs(spec): optional describe request, columns on an empty SELECT; 0.0.2 - #32

Open
ssilvius wants to merge 1 commit into
mainfrom
spec/describe
Open

ssilvius wants to merge 1 commit into
mainfrom
spec/describe

Conversation

@ssilvius

Copy link
Copy Markdown
Contributor

Summary

A client that keeps two databases in step has to learn each table's columns and primary key, and today http-sql gives it no portable way to do that. The spec is transport, not policy (section 10.1), so a conforming server may refuse PRAGMA or reads of its system tables, and a single-statement response carries column names only. This edit adds an optional describe request so the server answers from its own catalogue and the client sends no engine-specific SQL. It also says outright that a SELECT matching no rows still returns its column names. It is an additive edit, numbered 0.0.2.

Acceptance criteria mapping

1. Section 4 admits three request shapes; section 4.3 defines describe

The opening rule of section 4 now names sql, batch and describe and rejects a body with more than one of them, or none. New section 4.3 defines the request. The table name is never executed as SQL: a server that builds a catalogue query from it must bind or quote it. The server answers from its own catalogue, so a server whose policy refuses PRAGMA can still answer. A describe cannot appear inside a batch.
Evidence: SPEC.md:53 (section 4 rule), SPEC.md:110-124 (section 4.3), SPEC.md:120 (answered from the server's catalogue)

2. Section 6.3 defines the describe success response

The response is a columns array in declared order, the order SELECT * produces. Each column object carries name, type and key. type is the declared type, or null for a column declared without one, and the server must not map it onto another type system. key is the 1-based position in the primary key, so a composite key keeps its declared order. A table-level generatedKey is true when the database assigns the key itself; the examples given are SQLite's rowid alias, PostgreSQL serial or identity columns, and MySQL AUTO_INCREMENT.
Evidence: SPEC.md:230-251 (section 6.3), SPEC.md:249 (generatedKey)

3. A missing or hidden table returns one registered error

No registered code means "not found." permission_error (403) fits a table the token cannot see, and because the response for a missing table must be identical, that case gets permission_error too, so describe does not reveal whether a hidden table exists. The section 7 row's meaning widens to cover it, and no new code is registered. sql_error was the other candidate, and it is what a SELECT against a missing table returns (checks S-5). It was passed over because a describe carries no SQL. Section 12 repeats the identical-response rule for security readers.
Evidence: SPEC.md:124 (section 4.3 error rule), SPEC.md:279 (section 7 table row), the section 12 describe bullet

4. Section 6.1 requires columns for an empty SELECT

The columns field description now says that a SELECT produces a result set even when it matches no rows, so columns must carry its names while rows is empty. The old text implied this, so the new sentence makes it explicit and contradicts nothing.
Evidence: SPEC.md:177

5. Section 10.1 lets servers implement describe and says how to decline

The server MAY list gains a describe bullet. A server that implements describe answers with the section 6.3 envelope and with permission_error for a table it will not describe. A server that does not must reject the request with bad_request (400). Section 4 already gives that code to a body with no recognized field, and section 4 now says a server without describe treats such a request that way.
Evidence: SPEC.md:331, plus the second paragraph of section 4

6. Section 10.2 forbids clients from assuming describe

Client MUST item 2 now lists describe among the request fields. A new item 7 says a client must not assume describe exists. It also says a bad_request in answer to a well-formed describe means the server lacks the request, and the client must not read that error as an empty table.
Evidence: SPEC.md:339, SPEC.md:344

7. The version moves to 0.0.2, with a history entry

The title, the status line, the Http-Sql-Version and Http-Sql-Accept-Version examples in section 9, and the opening of section 11 now read 0.0.2. The new version-history entry lists the additions and says what a 0.0.1 server must change, consistent with section 11's rule that the patch number moves on any edit while the major is 0.
Evidence: SPEC.md:1, SPEC.md:5, SPEC.md:298, SPEC.md:353, SPEC.md:359

8. curl.md has a describe example

A new "Describe a table" section shows the curl request, a success response, and the 403 permission_error for a table that does not exist or that the token cannot see. It also notes that a server without describe answers 400.
Evidence: examples/curl.md:77

Not done

README.md, checks/README.md (H-1, H-2) and the example servers still say 0.0.1. The Cloudflare D1 example (examples/cloudflare-worker-to-d1/src/index.ts:119) returns columns: [] for a SELECT with zero rows, because D1 gives no column names when there are no rows. That breaks the clarified section 6.1. The example servers stay at 0.0.1 until they are fixed and the checks gain describe cases. Neither change was part of this issue.

Closes #31

🤖 Generated with Claude Code

A conforming server may refuse PRAGMA or reads of its system tables, and a
SELECT response carries column names only, so a client had no portable way
to learn a table's shape. The describe request asks the server for the
columns, declared types and primary key from its own catalogue. It is
optional; a server without it answers bad_request, and a client must not
assume it. A table that does not exist or that the token cannot see answers
the registered permission_error (403), one response for both.

Section 6.1 now says columns is required for a SELECT that matches no rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

spec: a client cannot learn a table's shape from a conforming server

1 participant