UNPKG

@opengis/fastify-table

Version:

core-plugins

187 lines (186 loc) 5.92 kB
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;