Files

50 lines
1.7 KiB
JavaScript

import _ from "lodash";
/**
* 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 not
* enforced during the sync, so cycles are safe.
*/
export default function orderDBSchema({ db_schema, }) {
const new_db_schema = _.cloneDeep(db_schema);
const tables = new_db_schema.tables;
const tables_by_name = new Map();
for (const table of tables) {
if (table)
tables_by_name.set(table.tableName, table);
}
const ordered = [];
const visited = new Set();
const visiting = new Set();
function visit(table) {
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;
}