Files
bun-sqlite/src/lib/schema/update-table.ts
T
tbenandClaude Opus 5.5 87da436f81 Port bun-mariadb conventions: init globals, db handler, split schema sync
- 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]>
2026-10-14 02:37:35 +01:00

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),
);
}
}