import { ClientBase, Pool, PoolClient } from 'pg'; import { IsolationLevel, Parameter, SQLFragment, TxnClient, param, transaction, } from 'zapatos/db'; export type DbClient = Pool | TxnClient; /** * Iterates through pgSettings properties, composes and runs a single SELECT * query to execute set_config for each property. */ export const setPgSettingsConfig = async ( pgSettings: { [key: string]: string } = {}, txn: Pool | TxnClient | PoolClient | ClientBase, ): Promise => { let literal = 'SELECT '; const params: Parameter[] = []; for (const property in pgSettings) { if (!Object.prototype.hasOwnProperty.call(pgSettings, property)) { continue; } params.push(param(property), param(pgSettings[property])); literal += 'set_config($, $, TRUE), '; } if (params.length > 0) { await new SQLFragment(`${literal.slice(0, -2)};`.split('$'), params).run( txn, ); // eslint-disable-next-line @typescript-eslint/no-explicit-any -- for debug purposes add the applied PG settings to the transaction object (txn as any)._debugPgSettings = pgSettings; } }; /** * Creates a transaction and sets pg settings before executing queries. * The applied pgSettings are added to the field "_debugPgSettings" in the transaction object for debug purposes. * @param pool - DB pool instance or PG/Zapatos client. * @param isolationLevel - Transaction isolation level. * @param pgSettings - Settings to be set before queries are executed. * @param callback - Callback where queries are executed. */ export const transactionWithContext = async ( pool: DbClient, isolationLevel: IsolationLevel, pgSettings: { [key: string]: string } = {}, callback: (client: TxnClient) => Promise, ): Promise => { return transaction(pool, isolationLevel, async (txn) => { await setPgSettingsConfig(pgSettings, txn); return callback(txn); }); };