| 39 | |
| 40 | // print SQL query |
| 41 | export function debugPgClient(pgClient: PoolClient, logger: Pino.Logger): PoolClient { |
| 42 | // If Postgres debugging is enabled, enhance our query function by adding |
| 43 | // a debug statement. |
| 44 | if (!pgClient[$$pgClientOrigQuery]) { |
| 45 | // Set the original query method to a key on our client. If that key is |
| 46 | // already set, use that. |
| 47 | pgClient[$$pgClientOrigQuery] = pgClient.query; |
| 48 | |
| 49 | pgClient.startExplain = () => { |
| 50 | pgClient._explainResults = []; |
| 51 | }; |
| 52 | |
| 53 | pgClient.stopExplain = async () => { |
| 54 | const results = pgClient._explainResults; |
| 55 | pgClient._explainResults = null; |
| 56 | if (!results) { |
| 57 | return Promise.resolve([]); |
| 58 | } |
| 59 | return ( |
| 60 | await Promise.all( |
| 61 | results.map(async (r) => { |
| 62 | const {result: resultPromise, ...rest} = r; |
| 63 | const result = await resultPromise; |
| 64 | const firstKey = result && result[0] && Object.keys(result[0])[0]; |
| 65 | if (!firstKey) { |
| 66 | return null; |
| 67 | } |
| 68 | const plan = result.map((r: any) => r[firstKey]).join('\n'); |
| 69 | return { |
| 70 | ...rest, |
| 71 | plan, |
| 72 | }; |
| 73 | }) |
| 74 | ) |
| 75 | ).filter((entry: unknown): entry is ExplainResult => !!entry); |
| 76 | }; |
| 77 | |
| 78 | pgClient.query = function (...args: Array<any>): any { |
| 79 | const [a, b, c] = args; |
| 80 | const variables: string[] = []; |
| 81 | // If we understand it (and it uses the promises API) |
| 82 | if ( |
| 83 | (typeof a === 'string' && (!c || typeof c === 'function') && (!b || Array.isArray(b))) || |
| 84 | (typeof a === 'object' && !b && !c) |
| 85 | ) { |
| 86 | if (pgClient._explainResults) { |
| 87 | const query = a && a.text ? a.text : a; |
| 88 | const values = a && a.text ? a.values : b; |
| 89 | if (query.match(/^\s*(select|insert|update|delete|with)\s/i) && !query.includes(';')) { |
| 90 | // Explain it |
| 91 | const explain = `explain ${query}`; |
| 92 | pgClient._explainResults.push({ |
| 93 | query, |
| 94 | values, |
| 95 | result: pgClient[$$pgClientOrigQuery] |
| 96 | .call(this, explain, values) |
| 97 | .then((data: any) => data.rows) |
| 98 | // swallow errors during explain |