Files
djhackersdev_minime/src/checkdb.ts
T
Tau d1d63e1ef9 chunithm: Initial implementation code drop
No cleaner development history than this exists. It's difficult to
methodically design something when you don't yet know what it is
you're designing.
2019-11-06 17:29:07 -05:00

137 lines
3.1 KiB
TypeScript

import { readdirSync, readFileSync } from "fs";
import logger from "debug";
import { resolve } from "path";
import sql from "sql-bricks-postgres";
import { DataSource, Transaction } from "./sql";
const scripts = [
// Order matters! Later scripts reference schema instantiated earlier.
"meta.sql",
"aime.sql",
"cm.sql",
"idz.sql",
];
const debug = logger("app:checkdb");
const initPath = "./schema/init";
const migratePath = "./schema/migrate";
const migrateRx = new RegExp("^M([0-9]{4})-.+\\.sql$");
async function initdb(txn: Transaction): Promise<void> {
// Get latest schemaver from migration script filenames
const migrations = readdirSync(migratePath);
let schemaver = 0;
for (const filename of migrations) {
const captures = filename.match(migrateRx);
if (captures === null) {
continue;
}
const migrationNo = parseInt(captures[1]);
if (schemaver < migrationNo) {
schemaver = migrationNo;
}
}
// Run init scripts
debug("Initializing database");
for (const script of scripts) {
debug("Executing %s", script);
const scriptPath = resolve(initPath, script);
const scriptSql = readFileSync(scriptPath, "utf-8");
await txn.raw(scriptSql);
}
// Set up metadata table
const metaInsert = sql.insert("meta", { schemaver });
await txn.modify(metaInsert);
debug("Initialized new database to schema version %s", schemaver);
}
async function migratedb(
txn: Transaction,
schemaver: number
): Promise<number | undefined> {
const filenames = readdirSync(migratePath);
let lastMigrationNo: number | undefined;
filenames.sort();
for (const filename of filenames) {
const captures = filename.match(migrateRx);
if (captures === null) {
debug("Warning: Unexpected file %s in SQL migrations dir", filename);
continue;
}
const migrationNo = parseInt(captures[1]);
if (migrationNo <= schemaver) {
continue;
}
debug("Executing database upgrade: %s", filename);
const scriptPath = resolve(migratePath, filename);
const scriptSql = readFileSync(scriptPath, "utf-8");
await txn.raw(scriptSql);
lastMigrationNo = migrationNo;
}
if (lastMigrationNo !== undefined) {
const metaUpdate = sql.update("meta", { schemaver: lastMigrationNo });
await txn.modify(metaUpdate);
}
return lastMigrationNo;
}
export default async function checkdb(db: DataSource): Promise<void> {
const stmt = sql.select("schemaver").from("meta");
let maybe: number | undefined;
try {
const row = await db.transaction(txn => txn.fetchRow(stmt));
if (row !== undefined) {
maybe = parseInt(row.schemaver);
}
} catch (e) {
return db.transaction(initdb);
}
if (maybe === undefined) {
throw new Error(
"Database corrupted: `meta` table singleton row is missing"
);
}
const schemaver = maybe;
const newver = await db.transaction(txn => migratedb(txn, schemaver));
if (newver !== undefined) {
debug("Upgraded database to version %s", newver);
await db.vacuum();
debug("Post-upgrade housekeeping complete");
}
}