Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ HTTP status: `200`.
- `columns` (REQUIRED, array of strings) — the column names of the result, in the order produced by the SQL engine. Empty array for statements that produce no result set (INSERT, UPDATE, DELETE, DDL).
- `rows` (REQUIRED, array of arrays) — each inner array has the same length as `columns`, with values in column order. Values use the same JSON / tagged-value encoding as section 5. Empty array if no rows.
- `rowsAffected` (REQUIRED, integer) — the number of rows changed by the statement. `0` for SELECT.
- `lastInsertId` (OPTIONAL, string, number, or null) — the identifier of the most recently inserted row when the server can determine it (typically the auto-increment id). `null` when not applicable or not available.
- `lastInsertId` (OPTIONAL, string or null) — the identifier of the most recently inserted row when the server can determine it (typically the auto-increment id). Always a string, never a JSON number: when the identifier is an integer it is encoded as a decimal string, consistent with the `bigint` encoding in section 5, so 64-bit ids survive clients whose numbers are IEEE-754 doubles. Text identifiers (a UUID primary key, say) are carried as-is. `null` when not applicable or not available.

The arrays-of-arrays shape (not arrays-of-objects) is normative. It keeps payloads compact, makes column order explicit, and supports duplicate column names from joins.

Expand Down
2 changes: 1 addition & 1 deletion conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Conformance is self-asserted. The community can call out failures via issues.
## Optional / "nice to have"

- Vendor error codes carry the `vendor:` prefix.
- `lastInsertId` is populated for INSERT statements where the SQL engine reports it.
- `lastInsertId` is populated for INSERT statements where the SQL engine reports it, as a JSON string (integer ids as decimal strings) or `null` — never a JSON number.
- Rate-limited responses return `error.code` = `rate_limited` and HTTP 429.
- Server enforces a maximum result row count and returns `payload_too_large` past it.

Expand Down
7 changes: 5 additions & 2 deletions examples/cloudflare-worker-to-d1/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface StatementResult {
columns: string[];
rows: unknown[][];
rowsAffected: number;
lastInsertId?: string | number | null;
lastInsertId?: string | null;
}

const VERSION = "0.1";
Expand Down Expand Up @@ -107,11 +107,14 @@ async function runBatch(db: D1Database, batch: Statement[], atomic: boolean): Pr
function projectD1Result(res: D1Result): StatementResult {
const rows = (res.results ?? []) as Record<string, unknown>[];
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
// SPEC 6.1: lastInsertId is a string or null, never a JSON number, so 64-bit
// rowids survive clients whose numbers are IEEE-754 doubles.
const lastRowId = res.meta?.last_row_id;
return {
columns,
rows: rows.map((r) => columns.map((c) => encodeValue(r[c]))),
rowsAffected: res.meta?.changes ?? 0,
lastInsertId: res.meta?.last_row_id ?? null,
lastInsertId: lastRowId === undefined || lastRowId === null ? null : String(lastRowId),
};
}

Expand Down
Loading