/** * How a restore, fork or carry loads a dump's `_substrat_*` spine tables (#1883). One * definition for both adapters' loaders, so the DO and the pure host build the same tables * from the same dump. * * A dump used to be replayed table by table, `CREATE TABLE` from the dump and then its rows, * spine included; the kernel's `CREATE TABLE IF NOT EXISTS` afterwards filled only the gaps. * So a dump could decide the schema of the tables the permission checker reads. A dump that * declared `_substrat_tuples.object` as `COLLATE NOCASE` made every `=` the checker and the * spine SQL wrote on it ignore case: a grant on `aiTurn:x` answered for `aiturn:x`. * * Now the loader builds every spine table from its own kernel DDL, and the dump contributes * rows only, inserted by column name: * - a column the kernel's table does not have (a dump from a newer kernel) is added as a plain * untyped column, unless it is named for SQLite's rowid (refused, since it would shadow it), with no type, collation, constraint or default, and keeps its values * (`spineColumnAdditions`). It is not one the checker reads, and it carries nothing that * could change how the columns the checker does read compare. When a later kernel adds the * column for real, its additive ALTER meets it and tolerates it as a duplicate; * - a column the dump lacks takes the kernel's default (NULL for every additive column), so * a dump taken before a column existed still restores. The one column that is not additive, * `_substrat_schedule_state.kind` (#1288, part of the key), is derived from the row the way * the #1288 rebuild derives it; * - a spine table the kernel does not build is refused (`assertSpineTablesBuilt`), every such * table named at once. * * In a scope, tables outside the spine keep the dump's own DDL: a vertical's schema is the * dump's to say. A directory has no vertical tables, so there every table is built by the * host and loaded by these same rules, and a table it does not build is refused * (`assertDirectoryTablesBuilt`, #1912). */ /** * A kernel-owned table, which a restore builds from the kernel's DDL rather than the dump's. * The prefix is the spine guard's, compared the way SQLite compares table names, without case: * a dump's `_Substrat_tuples` IS the tuples table, and read as a vertical one its DDL would be * replayed and the kernel's `IF NOT EXISTS` would skip its own. */ export declare function isSpineTable(name: string): boolean; /** * Refuse a dump carrying `_substrat_*` tables this host's kernel does not build, naming every * one of them. Called before any spine row goes in, inside the load's transaction; `columnsOf` * is the same lookup `dumpRowsInsert` takes, `undefined` for a table the kernel did not build. * * The usual cause is a dump from the other kind of host: a Durable Object's scope builds spine * tables (`_substrat_roles`, `_substrat_tenant_tuples`, …) that a node scope keeps in its * directory instead. Loading one anyway would mean dropping its rows, which a restore does not * do silently. * * Every `_substrat*` name is judged, the search index's namespace included (`namesSpineTable`, * not `isSpineTable`): a scope restore has skipped its index tables before this, since it * rebuilds them. A directory restore judges every table instead (`assertDirectoryTablesBuilt`). */ export declare function assertSpineTablesBuilt(names: readonly string[], columnsOf: KernelColumnsOf): void; /** A table's columns as the kernel built it here, or `undefined` when it built no such table. */ export type KernelColumnsOf = (name: string) => readonly string[] | undefined; /** * The `INSERT` that loads one dumped table's rows, one row of positional parameters per * execution, in the dump's column order: `spineRowsInsert` for a spine table, and the dump's * own columns for a vertical one. A directory builds every table, so it calls * `spineRowsInsert` directly. The names are quoted as given: the loader has already passed * the dump through `assertReplayableDump`. */ export declare function dumpRowsInsert(table: { name: string; columns: readonly string[]; }, columnsOf: KernelColumnsOf): string; /** * The statements that add to a table the host built (a spine table, or any directory table, * #1912) each column the dump carries and the host's table does not, as a plain untyped column: * `ADD COLUMN ""`, lowercased, and nothing after it, so it has no type, collation, * constraint or default. Run after `assertSpineTablesBuilt` / `assertDirectoryTablesBuilt` and * before the table's rows go in, inside the load's transaction. The names are the dump's, which * `assertReplayableDump` has already held to the identifier rule. Empty for a table the host * did not build, which those have refused. */ export declare function spineColumnAdditions(table: { name: string; columns: readonly string[]; }, kernelColumns: readonly string[] | undefined): string[]; /** * The naming columns a scope row from before the directory left NULL, filled with the defaults * `resolveScopeRecord` applies: a ULID lowercases into a valid slug, so the placeholder is unique * by construction. Run by both adapters' schema pass on every start, and by a directory restore * after its rows are in, inside its transaction (#1912). One statement per entry, since a * Durable Object's `exec` and better-sqlite3's `prepare` each take one. */ export declare const LEGACY_SCOPE_ROWS_BACKFILL: readonly string[]; /** * Load a directory dump's rows into the tables the host has just built from its own schema * (#1912): the one sequence both adapters' directory restores run, inside their transaction, * after dropping the old directory and running the schema pass. * * 1. refuse a table the directory does not build (`assertDirectoryTablesBuilt`), and one that * would pass `maxColumns` once the dump's unknown columns join this code's own; * 2. add each column the dump carries and this code does not know, bare and lowercased, and * refuse one named for the rowid (`spineColumnAdditions`); * 3. insert every row by column name, against the columns read back after step 2 * (`spineRowsInsert`); * 4. fill a pre-directory scope row's naming columns (`LEGACY_SCOPE_ROWS_BACKFILL`); * 5. backfill each kill switch's record (the schedule switch's, #1674, and the peer switch's, * #2029) from the dump's own admin log, when the dump predates that record. One that carried * it keeps its rows and is never backfilled over; * 6. backfill the membership removal fence the same way, when the dump predates it (#1184), * stamped `host.now()`: the restoring adapter's clock, at load time. * * A throw anywhere is the caller's transaction rolling back, so the directory keeps what it held. */ export declare function loadDirectoryDump(tables: readonly { name: string; columns: readonly string[]; rows: readonly (readonly unknown[])[]; }[], host: { columnsOf: KernelColumnsOf; exec: (sql: string) => void; insert: (sql: string, rows: readonly (readonly unknown[])[]) => void; /** * The most columns a table here may hold: a Durable Object's cap. `assertReplayableDump` * holds the dump's own column list to it, but a table grows by this code's columns plus the * dump's unknown ones, which can pass it while each list alone does not. */ maxColumns?: number; /** The adapter's clock — what a removal fence backfilled from a pre-fence dump stands at (#1184). */ now: () => string; }): void; //# sourceMappingURL=spine-restore.d.ts.map