@opengis/fastify-table
Version:
core-plugins
187 lines (186 loc) • 5.92 kB
JavaScript
import pg from "pg";
import { createHash } from "node:crypto";
import config from "../../../../config.js";
import getRedis from "../../redis/funcs/getRedis.js";
import logger from "../../logger/getLogger.js";
const rclient = getRedis({ db: 0 });
async function init(client) {
if (!client)
return;
// for unit tests
const options = client.options || {
database: client.database,
user: client.user,
password: client.password,
port: client.port,
host: client.host,
};
if (!client || !client.query || !client.connect) {
return;
}
const pgType = await client
.query(`SELECT json_object_agg(t.oid:: text, pg_catalog.format_type(t.oid, NULL)) FROM pg_catalog.pg_type t`)
.then((el) => el.rows[0]?.json_object_agg || {});
const pks = await client
.query(`
SELECT
connamespace::regnamespace::text,
conrelid::regclass,
(
SELECT attname FROM pg_attribute WHERE attrelid = c.conrelid AND attnum = c.conkey[1]
) as pk
FROM pg_constraint c
WHERE contype = 'p'
AND connamespace::regnamespace::text NOT IN ('sde')`)
.then((el) => el.rows || []);
const pk = pks.reduce((acc, curr) => ({
...acc,
[curr.connamespace === "public"
? `${curr.connamespace}.${curr.conrelid}`
: curr.conrelid]: curr.pk,
}), {});
const tlist = await client
.query(`SELECT
array_agg(
(
SELECT
nspname
FROM
pg_namespace
WHERE
oid = relnamespace
) || '.' || relname
) tlist
FROM
pg_class
WHERE
relkind IN ('r', 'v', 'm')`)
.then((d) => d.rows[0].tlist);
const rows = await client
.query(`SELECT
(
SELECT
nspname
FROM
pg_namespace
WHERE
oid = relnamespace
) || '.' || relname AS tname,
relkind
FROM
pg_class
WHERE
relkind IN ('r', 'v', 'm')`)
.then((el) => el.rows || []);
const relkinds = rows.reduce((acc, curr) => Object.assign(acc, { [curr.tname]: curr.relkind }), {});
async function query(q, args = []) {
if (!client)
throw new Error("empty pg client");
try {
const data = await client.query(q, args);
return data;
}
catch (err) {
// canceling statement due to statement timeout
if (err.code === "57014") {
logger.file("timeout/query", { q, stack: err.stack });
}
throw err;
}
}
async function querySafe(q, param) {
const pg1 = new pg.Pool({
...options,
statement_timeout: param?.timeout || 100000000,
});
try {
const args = Array.isArray(param) ? param : param?.args || [];
const data = await pg1.query(q, args);
console.log("pg.querySafe ok", q);
return data;
}
catch (err) {
if (err.code === "57014") {
console.warn("⚠️ pg.querySafe timeout", q);
return { rows: [], timeout: true };
}
console.warn("⚠️ pg.querySafe error", q);
throw err;
}
finally {
pg1.end();
console.log("pg.querySafe released", q);
}
}
async function one(q, param) {
const data = await query(q, Array.isArray(param) ? param : param?.args || []);
const result = ((Array.isArray(data) ? data.pop() : data)?.rows || [])[0] || {};
return result;
}
async function queryNotice(q, args, cb = () => { }) {
if (!client)
throw new Error("empty pg client");
const clientCb = await client.connect();
clientCb.on("notice", (e) => {
cb(e.message);
});
let result;
try {
result = await clientCb.query(q, args);
clientCb.release();
}
catch (err) {
clientCb.release();
cb(err.toString(), 1);
throw err;
}
// client.end();
return result;
}
async function queryCache(q, param) {
const { table, args = [], time = 15 } = param || {};
const seconds = typeof time !== "number" || time < 0 ? 0 : time * 60;
if (seconds === 0 || config.disableCache) {
const data = await query(q, args || []);
return data;
}
// CRUD table state
const keyCacheTable = `pg:${table}:crud`;
const crudInc = table && config.redis ? (await rclient.get(keyCacheTable)) || 0 : 0;
//
const hash = createHash("sha1")
.update([q, JSON.stringify(args)].join())
.digest("base64");
const keyCache = `pg:${hash}:${crudInc}`;
const cacheData = config.redis ? await rclient.get(keyCache) : null;
if (cacheData && !config.local) {
// console.log('from cache', table, query);
return JSON.parse(cacheData);
}
const data = await query(q, args || []);
if (seconds > 0 && config.redis) {
rclient.set(keyCache, JSON.stringify(data), "EX", seconds);
}
// console.log('no cache', table, crudInc, query);
return data;
}
Object.assign(client, {
...options,
options,
one,
pgType,
pk,
tlist,
relkinds,
queryCache,
queryNotice,
querySafe,
});
console.log("New client init finished", client.database, new Date().toISOString());
logger.file("pg", {
message: "client init finished",
database: client.database,
});
}
// export default client;
export default init;