@opengis/fastify-table
Version:
core-plugins
161 lines (160 loc) • 7.12 kB
JavaScript
// Вигрузка схеми бд
// Usage examples:
// bun .\script\dump.js --table=bpmn.tasks
// bun .\script\dump.js --schema=bpmn
import path from "node:path";
import { existsSync } from "node:fs";
import { mkdir, writeFile, rm } from "node:fs/promises";
import { config, handlebars, pgClients, getTemplate } from "../utils.js";
import { build } from "../helper.js";
if (import.meta.main) {
dumpMigrateSQL();
}
const debug = false;
export default async function dumpMigrateSQL() {
const app = await build();
// app.addHook("onClose", async () => teardown());
try {
// const { database, host, port, user, password } = config.pg;
if (!config.pg) {
console.error("empty config.pg, skip...");
return null;
}
if (!Bun.argv[2]) {
console.error("missing schema / table name, skip...");
}
const [key, value] = Bun.argv[2]?.substring?.(2)?.split?.("=") || [];
const tableName = key === "table" ? value : null;
const schemaName = key === "schema" ? value : value?.split?.(".")?.shift?.();
const pg = pgClients.client;
// const pg = await getPGAsync({ database, host, port, user, password });
await pg.query(`select 1`);
const schemaExists = await pg
.query(`SELECT 1 FROM information_schema.schemata WHERE schema_name = $1`, [schemaName])
.then((el) => el.rowCount);
if (!schemaExists) {
console.error("Вказаної схеми не існує", config.pg?.database);
return null;
}
// if (tableName && !pg.pk?.[tableName]) {
// console.error('Вказаної таблиці не існує', config.pg?.database);
// return null;
// }
const dump = await schemaItem({
pg,
table: tableName,
schema: schemaName,
debug,
});
if (debug) {
console.log(dump);
return null;
}
const filepath = await saveFile(dump, tableName || schemaName || "");
console.log("success", filepath);
// app.close();
}
catch (err) {
console.error(err);
// app.close();
}
finally {
await app.close();
process.exit(0);
}
}
async function saveFile(data, filename) {
if (!data)
throw new Error(`no data - ${filename}`);
const filepath = path.join("log/dump", `${filename}.sql`);
const fileExists = existsSync(filepath);
// overwrite old file
if (fileExists) {
await rm(filepath, { force: true, recursive: true });
}
await mkdir(path.dirname(filepath), { recursive: true });
await writeFile(filepath, Buffer.from(data, "utf-8"));
return filepath;
}
async function schemaItem({ pg, table, schema, debug, }) {
if (!schema && !table)
return new Error("param schema is required");
const { rows: schemaInfo } = await pg.query(`select c.oid,relname,nspname,obj_description(c.oid) as description,
(
select json_agg(row_to_json(q))
from (
select
column_name,
case
when data_type='USER-DEFINED' AND udt_name='geometry' THEN 'geometry'
when data_type='ARRAY' AND udt_name='_text' THEN 'text[]'
when data_type='ARRAY' AND udt_name='_int4' THEN 'integer[]'
else data_type
end as data_type,
ordinal_position,
column_default,
is_nullable,
case
when column_name='uid' then 'ідентифікатор автора запису в БД'
when column_name='cdate' then 'Дата створення запису в БД'
when column_name='editor_id' then 'Ідентифікатор автора, який останій вніс зміни в запис'
when column_name='editor_date' then 'Час останії зміни в записі'
when column_name='files' then 'Системна колонка'
when column_name='doc_status' then 'Статус документа'
when column_name='reg_status' then 'Статус реєстрації'
when column_name='obj_version' then 'Версія запису'
else col_description(a.attrelid,ordinal_position)
end as description
from information_schema.columns col
LEFT JOIN pg_attribute a ON col.column_name=a.attname and c.oid = a.attrelid
where col.table_schema=nspname and col.table_name=relname
)q
) as columns
from pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
where ${table ? `nspname||'.'||relname='${table}'` : `'${schema}'=nspname`} and relam=2
order by nspname,relname`);
if (!schemaInfo?.length)
throw new Error("invalid params");
const { rows: constraints } = await pg.query(`select con.conrelid::regclass as constraint_table, a.column_name,
con.conname as constraint_name,contype as constraint_type, con.confrelid::regclass as foreign_table,
con.confupdtype, con.confdeltype, con.confmatchtype, u.column_name as foreign_column,
case when contype='c' then pg_get_constraintdef(con.oid, true) else null end AS check_definition from pg_constraint con
left join pg_class c ON c.oid = con.conrelid
left join pg_namespace n ON n.oid = c.relnamespace
left join lateral (
select string_agg(a.attname,',') as column_name from pg_attribute a
where con.conrelid = a.attrelid and a.attnum = any(con.conkey) limit 1
)a on 1=1
left join lateral (
select column_name from information_schema.constraint_column_usage u
where conname=u.constraint_name limit 1
)u on 1=1
where ${table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`}`);
// add table constraints, mermaid
schemaInfo?.forEach((row) => {
// constraint type to column
row?.columns?.forEach((col) => {
const { constraint_type } = constraints?.find((con) => con?.column_name === col?.column_name &&
con.constraint_table === `${row.nspname}.${row.relname}`) || {};
Object.assign(col, { constraint_type });
});
// table relations
const tableConstraints = constraints
?.filter((el) => el?.constraint_table === `${row.nspname}.${row.relname}`)
?.map((el) => ({
...el,
check_definition: el.check_definition
? new handlebars.SafeString(el.check_definition)
: null,
}));
Object.assign(row, { constraints: tableConstraints });
});
if (debug)
return schemaInfo;
const body = await getTemplate("pt", "schemaItem.pt");
const schemaContent = await handlebars.compile(typeof body === "string"
? body
: body?.hbs || "template not found schemaItem.pt")({ nspname: schema, rows: schemaInfo, constraints });
return schemaContent.replace(/'/g, "'");
}