Files
bun-mariadb/src/lib/schema/order-db-schema.ts
T
2026-10-14 01:47:55 +01:00

68 lines
2.0 KiB
TypeScript

import _ from "lodash";
import type {
BUN_MARIADB_DatabaseSchemaType,
BUN_MARIADB_TableSchemaType,
CreateDBSchemaParams,
} from "../../types";
/**
* Topologically order tables so referenced (parent) tables come before the
* tables that reference them. Declaration order is kept wherever the FK
* graph allows it.
*
* - Self references (e.g. `comments.parent_comment_id`) are ignored.
* - References to tables outside the schema are ignored.
* - Cycles do not throw: a table already on the current path is skipped,
* so every table is still emitted exactly once. Foreign keys are created
* after all tables exist, so cycles are safe to sync.
*/
export default async function orderDBSchema({
db_schema,
}: CreateDBSchemaParams): Promise<BUN_MARIADB_DatabaseSchemaType> {
const new_db_schema = _.cloneDeep(db_schema);
const tables = new_db_schema.tables;
const tables_by_name = new Map<string, BUN_MARIADB_TableSchemaType>();
for (const table of tables) {
if (table) tables_by_name.set(table.tableName, table);
}
const ordered: BUN_MARIADB_TableSchemaType[] = [];
const visited = new Set<string>();
const visiting = new Set<string>();
function visit(table: BUN_MARIADB_TableSchemaType) {
if (visited.has(table.tableName) || visiting.has(table.tableName)) {
return;
}
visiting.add(table.tableName);
for (const field of table.fields || []) {
const dst_table_name = field?.foreignKey?.destinationTableName;
if (!dst_table_name || dst_table_name === table.tableName) {
continue;
}
const fk_table = tables_by_name.get(dst_table_name);
if (fk_table) {
visit(fk_table);
}
}
visiting.delete(table.tableName);
visited.add(table.tableName);
ordered.push(table);
}
for (const table of tables) {
if (table) visit(table);
}
new_db_schema.tables = ordered;
return new_db_schema;
}