ddl-manager
Version:
store postgres procedures and triggers in files
164 lines (140 loc) • 4.96 kB
text/typescript
import { flatMap } from "lodash";
import { Table } from "./Table";
import { TableID } from "./TableID";
import { DatabaseTrigger } from "./DatabaseTrigger";
import { DatabaseFunction } from "./DatabaseFunction";
import { Migration } from "../../Migrator/Migration";
import { Index } from "./Index";
import { MAX_NAME_LENGTH } from "../postgres/constants";
export class Database {
readonly tables: Table[];
functions: DatabaseFunction[];
readonly aggregators: string[];
private tablesMap: Record<string, Table>;
private functionsMap: Record<string, DatabaseFunction[]>;
constructor(tables: Table[] = []) {
this.tablesMap = {};
this.tables = [];
for (let table of tables) {
table = table.clone();
this.tables.push(table);
this.tablesMap[ table.toString() ] = table;
}
this.functions = [];
this.functionsMap = {};
this.aggregators = [
"count",
"max",
"min",
"sum",
"avg",
"array_agg",
"string_agg",
"bool_or",
"bool_and",
"every"
];
}
getAllColumns() {
return flatMap(this.tables, table => table.columns);
}
getAllTriggers() {
return flatMap(this.tables, table => table.triggers);
}
getColumn(tableId: TableID, column: string) {
const dbTable = this.tables.find(table =>
table.name === tableId.name &&
table.schema === tableId.schema
);
return dbTable?.getColumn(column);
}
getTable(tableId: TableID) {
return this.tablesMap[ tableId.toString() ];
}
getTriggersByProcedure(procedure: {schema: string, name: string}) {
return flatMap(this.tables, (table) => {
return table.triggers.filter(trigger =>
trigger.procedure.schema === procedure.schema &&
trigger.procedure.name === procedure.name
);
});
}
setTable(table: Table) {
if ( !this.tablesMap[ table.toString() ] ) {
table = table.clone();
this.tables.push( table );
this.tablesMap[ table.toString() ] = table;
}
}
addFunctions(functions: DatabaseFunction[]) {
for (const func of functions) {
this.functions.push(func);
const name = func.name.slice(0, MAX_NAME_LENGTH);
this.functionsMap[ name ] ??= [];
this.functionsMap[ name ].push(func);
}
}
addTrigger(trigger: DatabaseTrigger) {
const table = this.getTable(trigger.table);
if ( !table ) {
throw new Error(`unknown table "${ trigger.table }" for trigger "${ trigger.name }"`)
}
table.addTrigger(trigger);
}
addIndex(index: Index) {
const table = this.getTable(index.table);
if ( !table ) {
throw new Error(`unknown table "${ index.table }" for index "${ index.name }"`)
}
table.addIndex(index);
}
applyMigration(migration: Migration) {
this.addFunctions(migration.toCreate.functions);
for (const trigger of migration.toCreate.triggers) {
this.addTrigger(trigger);
}
for (const column of migration.toCreate.columns) {
const table = this.getTable(column.table);
if ( table ) {
table.addColumn(column);
}
}
this.functions = this.functions.filter(existentFunc =>
!migration.toDrop.functions.some(dropFunc =>
existentFunc.getSignature() === dropFunc.getSignature()
)
);
for (const dropFunc of migration.toDrop.functions) {
const name = dropFunc.name.slice(0, MAX_NAME_LENGTH);
this.functionsMap[name] = this.functionsMap[name].filter(existentFunc =>
existentFunc.getSignature() !== dropFunc.getSignature()
);
}
for (const trigger of migration.toDrop.triggers) {
const table = this.getTable(trigger.table);
if ( table ) {
table.removeTrigger(trigger);
}
}
for (const column of migration.toDrop.columns) {
const table = this.getTable(column.table);
if ( table ) {
table.removeColumn(column);
}
}
}
addAggregators(newAggregators: string[]) {
for (const aggName of newAggregators) {
if ( !this.aggregators.includes(aggName) ) {
this.aggregators.push(aggName);
}
}
}
allCacheTriggers() {
return flatMap(this.tables, table => table.triggers)
.filter(trigger => !!trigger.cacheSignature);
}
getFunctions(name: string) {
return this.functionsMap[ name.slice(0, MAX_NAME_LENGTH) ] ?? [];
}
}