From 5bf6e123b99bc85bb663ebd12d4d878b9f5c2c03 Mon Sep 17 00:00:00 2001 From: Marc Villain Date: Mon, 2 Nov 2026 01:46:05 +0100 Subject: [PATCH] docs: dialect contract, decode and writes without RETURNING --- docs/writing-a-dialect.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/writing-a-dialect.md b/docs/writing-a-dialect.md index 702170f..f004b90 100644 --- a/docs/writing-a-dialect.md +++ b/docs/writing-a-dialect.md @@ -5,8 +5,9 @@ A dialect implements one interface: ```ts interface Dialect { name: string; - capabilities: { returning: boolean; estimatedCount: boolean; rowSecurity: boolean }; + capabilities: { returning: boolean; estimatedCount: boolean; rowSecurity: boolean; procedures: boolean }; renderer: SqlRenderer; + decode(value: unknown): unknown; introspect(scope: { schemas: string[]; tables: string[] }): Promise; transaction(fn: (tx: Tx) => Promise, options: { timeoutMs: number; readOnly: boolean }): Promise; mapError(err: unknown): RestError | null; @@ -19,6 +20,8 @@ interface Dialect { | `introspect` | reads tables, views, columns, primary keys, foreign keys and functions, and maps each type to a logical one: `text`, `integer`, `number`, `boolean`, `uuid`, `date`, `timestamp`, `time`, `json`, `binary`, `array` or `other` | | `transaction` | runs a request's statements in one transaction with a statement timeout | | `mapError` | turns an error the client caused into a stable code; anything else returns `null` and answers `internal` | +| `decode` | reads the JSON a statement returns: drivers that parse JSON return it as is, others parse text | +| `capabilities.returning` | `false` makes the core lock and read the keys of an update or delete first, and take an insert's keys from the body or `lastInsertId()` | | `renderer` | writes the SQL | The core decides everything else from the logical types: which operators a column accepts, which values are valid, which columns can be summed. @@ -44,8 +47,10 @@ The core decides everything else from the logical types: which operators a colum | `returning(resource)` | `RETURNING key` | | `writeTarget(resource, alias)` | `table AS alias` | | `call(fn, args)` | `SELECT to_json(fn(arg => $1))` | +| `selectKeys(resource, where)` | `SELECT key ... FOR UPDATE`, without RETURNING only | +| `lastInsertId()` | `SELECT LAST_INSERT_ID() AS id`, without RETURNING only | -Larger units can be overridden too: `relationSource`, `exists`, `filter`, `embed`, `read`, `count`, `insert`, `update`, `delete`. +Larger units can be overridden too: `relationSource`, `exists`, `filter`, `order`, `embed`, `read`, `count`, `insert`, `update`, `delete`. The four dialects in this repository are the worked examples: SQL Server overrides the most. Placeholders are written last: fragments carry internal tokens, and the final text gets the database's own placeholders in text order. A dialect with positional placeholders (`?`) gets its values in the right order whatever order the fragments were built in.