/** * The SQLite limits a Durable Object enforces and node does not (#1741). * * A vertical's `ctx.sql` runs on `better-sqlite3` in every local suite and on a Durable * Object's SQLite when it is deployed, and the DO's build sets several limits far below * stock SQLite's. A statement over one passed every node suite and failed only on a * deployed scope — `customer/get` in an adopter vertical counted rows with a 29-term * `UNION ALL` and broke on the first hosted call. #1655 closed the same gap for a `LIKE` * pattern (`tools/vitest/like-pattern-limit.cjs`, which has to patch the driver because * `better-sqlite3` exposes no `sqlite3_limit`). Three of the four limits below can be judged * from the statement text, so the adapter judges them itself, and a vertical's OWN suite sees * what a Durable Object sees. The pattern limit cannot: a pattern is often built at run time, * and judging it exactly means replacing `like()` on every connection, a JavaScript call per * row that also switches off SQLite's `LIKE` prefix index optimisation. That cost is fine in a * test preload and not in an adapter self-hosters run, so it stays a preload's job — published * as `@substrat-run/adapter-sqlite/testing` (#1770), on by default in the scaffold, so a * vertical opts in the same way for the one limit the adapter itself cannot judge. * * The values are MEASURED, not read from documentation: * `packages/adapter-cloudflare/test/do-sql-limits.test.ts` drives a real Durable Object's * SQL to each limit and pins the value and the refusal message it gave. If workerd moves * one, that test goes red first. * * | limit | value | DO's refusal | * |-------------------------------|----------------|-----------------------------------------------------| * | terms in one compound SELECT | 5 | `too many terms in compound SELECT` | * | bound parameters | 100 | `too many SQL variables at offset N` | * | statement length | 100 000 bytes | `statement too long` (`SQLITE_TOOBIG`) | * | `LIKE`/`GLOB` pattern length | 50 bytes | `LIKE or GLOB pattern too complex` (not judged here: preload only) | * | columns in a table | 100 | `too many columns on ` | * | columns in a result set | 100 | `too many columns in result set` | * * A TABLE's width cannot be judged from the text — a `SELECT *` is as wide as the tables under it, * and a table is as wide as its migrations left it — so the node adapter reads it from the * schema AFTER a migration or runtime DDL ran (#1811). A result set is judged twice: the * outermost width from the driver's prepared statement (which sees a `*`), and every select * core's WRITTEN columns from the text, because the limit is on every core and the driver * reports only the outermost (`SELECT c0 FROM (SELECT 0, …, 100)` is refused hosted). What * neither sees is a `*` inside an inner SELECT: that gap is documented, not judged. * * What is NOT a limit, measured: a multi-row `VALUES` list — SQLite does not count its rows * as compound terms; 5 000 rows ran on the DO. So `INSERT … VALUES (…),(…),…` is bounded by * the parameter count and the statement length only. * * Judged here, from the text, exactly as SQLite counts: * - **compound terms** — `UNION`, `UNION ALL`, `INTERSECT` and `EXCEPT` at one parenthesis * level chain their operands into one compound; the limit is on the operands (operators + 1). * A subquery, a CTE body and a view body each start their own count. * - **bound parameters** — SQLite's own numbering: each `?` takes the next number, `?N` * takes N, a `:name`/`@name`/`$name` takes the next number the first time it is seen. The * limit is on the highest number, not on how many are written or how many are bound. * - **statement length** — the whole string the caller passed, in UTF-8 bytes (a multi- * statement string counts once, as on the DO). * * The messages are the DO's own, suffix included, so a suite that matches on one matches * both adapters. Only MODULE-facing SQL is wrapped: platform-internal statements are the * platform's own responsibility, and they already run on a real DO in the workerd suites. */ import type { ScopedSql } from './scope-host.js'; /** The limits a Durable Object's SQLite enforces — measured, see the file header. */ export declare const DO_SQL_LIMITS: { /** Terms (operands) in one compound `SELECT`. */ readonly compoundTerms: 5; /** Bound parameters — the highest parameter number — in one statement. */ readonly boundParameters: 100; /** Statement length, in UTF-8 bytes, of the whole string handed to `sql.exec`. */ readonly statementBytes: 100000; /** `LIKE`/`GLOB` pattern length, in UTF-8 bytes. Enforced by `tools/vitest/like-pattern-limit.cjs`. */ readonly likePatternBytes: 50; /** Columns in one table, and in one result set. Measured by a 1–2001 binary search on workerd (#1811). */ readonly columns: 100; }; /** * Refuse a statement a Durable Object's SQLite would refuse for its length, its compound * `SELECT` terms, or its bound parameters. Throws the DO's own message. * * Not in the kernel's index, but exported from this module on purpose: * `tools/vitest/sql-limits.cjs` requires `dist/sql-limits.js` by file path to judge every * statement a node suite prepares, so dropping this `export` would break that preload. */ export declare function assertWithinSqlLimits(sql: string): void; /** * The refusal of a result set wider than `DO_SQL_LIMITS.columns`: the DO's own words, then what * the DO does not say. (A DO names a table it refuses `sqlite_altertab_` for an `ADD COLUMN`, * so the two adapters do not share the table message exactly; a suite matches its prefix.) */ export declare const tooManyResultColumns: (width: number) => string; /** The refusal of a table wider than `DO_SQL_LIMITS.columns`, counted as the DO counts. */ export declare const tooManyTableColumns: (table: string, width: number) => string; /** * Wrap a module-facing `ScopedSql` so every statement passes `assertWithinSqlLimits` first. * The node adapter wraps `ctx.sql` in it; the DO needs no wrapper — its SQLite is the * source of these limits. */ export declare function guardSqlLimits(inner: ScopedSql): ScopedSql; //# sourceMappingURL=sql-limits.d.ts.map