- isQueue table option adds queue columns and a (status, available_at) index; BunSQLite.queue exposes add, claim, complete, fail and release_stale with atomic claims - Set busy_timeout on connections so workers in separate processes wait instead of failing with SQLITE_BUSY - Quote table and column names with backticks in generated SQL, so reserved words (to, order, group) work and unknown columns error instead of matching as string literals - Tests for queue ops, multi-process claims and reserved-word names Co-Authored-By: Claude Opus 5.5 <[email protected]>
72 lines
2.5 KiB
JavaScript
72 lines
2.5 KiB
JavaScript
import _ from "lodash";
|
|
import createDBManagerTable from "./create-db-manager-table";
|
|
import dropRemovedTables from "./drop-removed-tables";
|
|
import getExistingTablesFromTablesManagerTable from "./get-existing-tables-from-tables-manager-table";
|
|
import handleDBSchemaTable from "./handle-db-schema-table";
|
|
import orderDBSchema from "./order-db-schema";
|
|
/**
|
|
* # Sync the database to the schema
|
|
* @description Changed tables are rebuilt by copying into a new table and
|
|
* dropping the old one. With foreign keys ON, `DROP TABLE` runs an implicit
|
|
* `DELETE FROM` that fires `ON DELETE CASCADE` on child tables, so keys are
|
|
* disabled for the sync and checked before committing. (`PRAGMA
|
|
* foreign_keys` is a no-op inside a transaction.) With `dry_run` the
|
|
* transaction is rolled back, so `changes` is the exact plan.
|
|
*/
|
|
export default function createDBSchema(params) {
|
|
const { db, dry_run } = params;
|
|
/**
|
|
* Reorder Tables (parents before children with FKs)
|
|
*/
|
|
const db_schema = orderDBSchema(params);
|
|
for (const table of db_schema.tables) {
|
|
if (table.isQueue && table.isVector) {
|
|
throw new Error(`Table "${table.tableName}" can't be both a queue and a vector table`);
|
|
}
|
|
}
|
|
db.run("PRAGMA foreign_keys = OFF;");
|
|
db.run("BEGIN");
|
|
try {
|
|
/**
|
|
* Create Schema Manager Table
|
|
*/
|
|
createDBManagerTable({ db });
|
|
const existing_tables = getExistingTablesFromTablesManagerTable({
|
|
db,
|
|
});
|
|
/**
|
|
* Create or update tables
|
|
*/
|
|
for (const table of db_schema.tables) {
|
|
handleDBSchemaTable({
|
|
...params,
|
|
db_schema,
|
|
table,
|
|
existing_tables,
|
|
});
|
|
}
|
|
/**
|
|
* Drop tables that no longer exist in schema
|
|
*/
|
|
dropRemovedTables({
|
|
...params,
|
|
existing_tables,
|
|
schema_tables: db_schema.tables.map((t) => t.tableName),
|
|
});
|
|
const fkViolations = db.query(`PRAGMA foreign_key_check`).all();
|
|
if (fkViolations.length) {
|
|
const tables = _.uniq(fkViolations.map((v) => `${v.table} -> ${v.parent}`));
|
|
throw new Error(`Foreign key violations after schema sync: ${tables.join(", ")}`);
|
|
}
|
|
db.run(dry_run ? "ROLLBACK" : "COMMIT");
|
|
}
|
|
catch (error) {
|
|
if (db.inTransaction)
|
|
db.run("ROLLBACK");
|
|
throw error;
|
|
}
|
|
finally {
|
|
db.run("PRAGMA foreign_keys = ON;");
|
|
}
|
|
}
|