/** * Where one SQL statement ends and the next begins — a faithful port of SQLite's own * `sqlite3_complete()` (src/complete.c), the state machine SQLite uses to decide whether a * string holds a complete statement. * * Every place the kernel or an adapter has to run SQL one statement at a time goes through * `splitSqlStatements`: a Durable Object's `exec` takes one statement, and the table-ownership * record (#2068) diffs the schema around each one. An ad-hoc splitter disagreed with SQLite on * valid SQL — a `CASE … END;` inside a trigger body ended the trigger early, a `;` inside a * quoted identifier split it, and a comment between two keywords glued them together — so this * one does not interpret the language at all beyond what `sqlite3_complete()` does: * * - tokens: `;`; whitespace and comments (`--…`, `/* … *\/`); string and quoted identifiers * (`'…'`, `"…"`, `` `…` ``, `[…]`); identifier words, of which only CREATE, TEMP/TEMPORARY, * TRIGGER, END and EXPLAIN matter; and any other single character; * - states: the transition table below, verbatim, which carries `CREATE [TEMP] TRIGGER … END;` * through the semicolons inside its body and through a `CASE … END` there; * - a statement ends at a `;` that returns the machine to START. * * It never rewrites text: each statement is the ORIGINAL substring, comments preserved, with only * the surrounding whitespace and the terminating `;` removed. Text that holds no token but * whitespace and comments is no statement. An unterminated string or comment ends the input as * one last statement, for SQLite to refuse when it runs. * * **Source.** `sqlite3_complete()` in `src/complete.c` of the SQLite source tree — the build * without SQLITE_OMIT_TRIGGER, whose table carries the trigger states. It was ported from that * function's documented token classes and transition table, not machine-translated, and it is held * to SQLite by behaviour rather than by a version stamp: `SPLIT_CASES` runs differentially against * the SQLite better-sqlite3 bundles (3.53.4 at the time of writing) and statement by statement in * workerd's (3.47.0). If SQLite ever changes what completes a statement, those tests are where it * shows. * * **Why this is not built on `tokenizeSql` (contracts), and should not be "unified" with it.** * The two answer different questions. `tokenizeSql` finds the NAMES a statement touches, for the * spine guard and the dump checks: it folds string literals and quoted identifiers into one * "quoted" class, merges dotted names, drops whitespace and most punctuation, and carries no source * offsets. Where a statement ENDS is a different grammar — SQLite's own, with its trigger nesting — * and it needs offsets into the original text so nothing is ever spliced. Re-deriving that rule * over a tokenizer built for the other job is how the hand-rolled splitter this replaced came to * cut `CASE … END;`, split a quoted `;` and glue `CREATE/*c*\/TABLE`. One splitter, and it is * SQLite's rule. */ /** * The statements in `sql`, in order, each its original text without the terminating `;`. * Name and signature are stable: every caller that needs a statement's TEXT — to log it, to show * it, to hash it, to read which tables it names — uses it. */ export declare function splitSqlStatements(sql: string): string[]; /** * The same statements, at the same boundaries, as the text to EXECUTE: every comment replaced * by whitespace of the same length (a newline stays a newline), strings and quoted identifiers * untouched. Every caller that runs SQL statement by statement — a module's migrations, the * kernel's own DDL, on both adapters — runs this, never `splitSqlStatements`' text. * * Why: SQLite stores a `CREATE TABLE`'s text as written, and its `ALTER TABLE … DROP COLUMN` * rewrites that stored text. workerd's SQLite fails the rewrite with "incomplete input" when the * dropped column is the last one and line comments come before it — so a module whose migration * created a commented table could never drop that column later, and a failed migration closes the * scope. Blanking the comments before execution keeps the stored DDL free of them on both hosts. * The one scanner decides what a comment is, so there is no second grammar to drift. */ export declare function executableSqlStatements(sql: string): string[]; /** * The whole of `sql` with every comment replaced by whitespace of the same length (a newline * stays a newline), strings and quoted identifiers untouched — `executableSqlStatements` without * the split. For a caller that executes one statement it was handed as text and must keep * executing it exactly as it does — a dump restore replaying a table's recorded DDL, which * `sqlite_master` holds verbatim, comments included — so the stored DDL it leaves carries none. */ export declare function blankSqlComments(sql: string): string; //# sourceMappingURL=sql-statements.d.ts.map