- init() loads config, schema and the SQLite client once into globals - all CRUD queries go through lib/db-handler.ts (optional per-call config) - split db-schema-manager.ts into lib/schema/* with FK-aware table ordering - add bun:test tests for schema ordering, FK constraints and schema sync - config/schema files can be .ts/.js/.json/.yaml/.yml; db_schema_file_name is now optional (defaults to schema.* in db_dir) - add export/import commands (database snapshot + schema archives) - package exports map, type re-exports, hardened publish.sh - validate table name in delete Co-Authored-By: Claude Opus 5.5 <[email protected]>
92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
import type {
|
|
BUN_SQLITE_TableSchemaType,
|
|
CreateDBSchemaParams,
|
|
} from "../../types";
|
|
import { sqlQuoteIdent as q } from "../../utils/sql-safe";
|
|
import checkIfTableExists from "./check-if-table-exists";
|
|
import createTable from "./create-table";
|
|
import getTableColumns from "./get-table-columns";
|
|
import resolveTable from "./resolve-table";
|
|
|
|
type Params = CreateDBSchemaParams & {
|
|
table: BUN_SQLITE_TableSchemaType;
|
|
};
|
|
|
|
/**
|
|
* # Update an existing table
|
|
* @description SQLite has limited ALTER TABLE support, so the table is
|
|
* rebuilt: create a temp table with the new schema, copy the data over,
|
|
* drop the old table and rename the temp table.
|
|
*/
|
|
export default function updateTable(params: Params) {
|
|
const { db, db_schema, table } = params;
|
|
|
|
console.log(`Updating table: ${table.tableName}`);
|
|
|
|
if (table.isVector) {
|
|
recreateVectorTable(params);
|
|
return;
|
|
}
|
|
|
|
const tempTableName = `${table.tableName}_temp_${Date.now()}`;
|
|
|
|
// Columns are matched by name only: SQLite converts values to the new
|
|
// column affinity on copy, so a type change keeps the data instead of
|
|
// silently dropping the column.
|
|
const existingColumns = getTableColumns({ db, table_name: table.tableName });
|
|
const columnsToKeep = resolveTable({ table, db_schema })
|
|
.fields.filter(
|
|
(f) =>
|
|
f.fieldName &&
|
|
existingColumns.find((c) => c.name == f.fieldName),
|
|
)
|
|
.map((f) => f.fieldName as string);
|
|
|
|
// Create temp table with new schema
|
|
createTable({ ...params, table: { ...table, tableName: tempTableName } });
|
|
|
|
// Copy data if there are common columns
|
|
if (columnsToKeep.length > 0) {
|
|
const columnList = columnsToKeep.map(q).join(", ");
|
|
db.run(
|
|
`INSERT INTO ${q(tempTableName)} (${columnList}) SELECT ${columnList} FROM ${q(table.tableName)}`,
|
|
);
|
|
}
|
|
|
|
db.run(`DROP TABLE ${q(table.tableName)}`);
|
|
db.run(`ALTER TABLE ${q(tempTableName)} RENAME TO ${q(table.tableName)}`);
|
|
}
|
|
|
|
/**
|
|
* Vector tables are only rebuilt when `recreate_vector_table` is set.
|
|
*/
|
|
function recreateVectorTable(params: Params) {
|
|
const { db, table, recreate_vector_table } = params;
|
|
|
|
if (!recreate_vector_table) {
|
|
return;
|
|
}
|
|
|
|
let existingRows: { [k: string]: any }[] = [];
|
|
|
|
if (checkIfTableExists({ db, table_name: table.tableName })) {
|
|
existingRows = db
|
|
.query(`SELECT * FROM ${q(table.tableName)}`)
|
|
.all() as { [k: string]: any }[];
|
|
|
|
db.run(`DROP TABLE ${q(table.tableName)}`);
|
|
}
|
|
|
|
createTable(params);
|
|
|
|
for (const row of existingRows) {
|
|
const columns = Object.keys(row);
|
|
const placeholders = columns.map(() => "?").join(", ");
|
|
|
|
db.run(
|
|
`INSERT INTO ${q(table.tableName)} (${columns.map(q).join(", ")}) VALUES (${placeholders})`,
|
|
Object.values(row),
|
|
);
|
|
}
|
|
}
|