diff --git a/package.json b/package.json index 74fa6f90..0076e271 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "pg-copy-streams": "^5.1.1", "pg-copy-streams-binary": "^2.0.1", "pg-cursor": "^2.7.0", + "pg-minify": "^1.6.2", "postgres-array": "^3.0.1", "postgres-interval": "^3.0.0", "promise-deferred": "^2.0.3", diff --git a/src/routines/executeQuery.ts b/src/routines/executeQuery.ts index cba8dbaa..f655b99c 100644 --- a/src/routines/executeQuery.ts +++ b/src/routines/executeQuery.ts @@ -23,6 +23,7 @@ import { import { createQueryId, normaliseQueryValues, + removeCommentedOutBindings, } from '../utilities'; import type { ClientConfigurationType, @@ -191,6 +192,8 @@ export const executeQuery = async ( } } + actualQuery = removeCommentedOutBindings(actualQuery); + let result: GenericQueryResult | null; for (const interceptor of clientConfiguration.interceptors) { diff --git a/src/utilities/index.ts b/src/utilities/index.ts index e0909bd2..2500887f 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -22,6 +22,9 @@ export { export { normaliseQueryValues, } from './normaliseQueryValues'; +export { + removeCommentedOutBindings, +} from './removeCommentedOutBindings'; export { stripArrayNotation, } from './stripArrayNotation'; diff --git a/src/utilities/removeCommentedOutBindings.ts b/src/utilities/removeCommentedOutBindings.ts new file mode 100644 index 00000000..0ecb5a5f --- /dev/null +++ b/src/utilities/removeCommentedOutBindings.ts @@ -0,0 +1,58 @@ +// @flow + +import minify from 'pg-minify'; +import type { + QueryType, +} from '../types'; + +const matchAllBindings = (sql: string) => { + return Array + .from( + sql.matchAll(/(\$\d+)/g), + ) + .map((match) => { + return Number(match[0].slice(1)); + }) + .sort((a, b) => { + return a - b; + }); +}; + +export const removeCommentedOutBindings = (query: QueryType): QueryType => { + const minifiedSql = minify(query.sql); + const originalBindings = matchAllBindings(query.sql); + const actualBindings = matchAllBindings(minifiedSql); + + let finalSql = minifiedSql; + + const finalValues = []; + + let lastFoundBinding = 0; + + for (const originalBinding of originalBindings) { + if (actualBindings.includes(originalBinding)) { + lastFoundBinding = originalBinding; + + finalValues.push(query.values[originalBinding - 1]); + + continue; + } + + const greatestBounding = lastFoundBinding; + + finalSql = finalSql.replace(/(\$\d+)/g, (match) => { + const matchedBinding = Number(match.slice(1)); + + if (matchedBinding > greatestBounding) { + return '$' + String(matchedBinding - 1); + } else { + return match; + } + }); + } + + return { + sql: finalSql, + values: finalValues, + }; +}; diff --git a/test/slonik/utilities/removeCommentedOutBindings.ts b/test/slonik/utilities/removeCommentedOutBindings.ts new file mode 100644 index 00000000..8c8dee35 --- /dev/null +++ b/test/slonik/utilities/removeCommentedOutBindings.ts @@ -0,0 +1,91 @@ +import test from 'ava'; +import { + removeCommentedOutBindings, +} from '../../../src/utilities/removeCommentedOutBindings'; + +test('removes commented out bindings', (t) => { + t.deepEqual( + removeCommentedOutBindings({ + sql: 'SELECT $1\n-- $2\n$3', + values: [ + 'foo', + 'bar', + 'baz', + ], + }), + { + sql: 'SELECT $1 $2', + values: [ + 'foo', + 'baz', + ], + }, + ); +}); + +test('removes multiple commented out bindings', (t) => { + t.deepEqual( + removeCommentedOutBindings({ + sql: 'SELECT $1\n-- $2\n$3\n-- $4\n$5', + values: [ + 'foo', + 'bar', + 'baz', + 'qux', + 'quux', + ], + }), + { + sql: 'SELECT $1 $2 $3', + values: [ + 'foo', + 'baz', + 'quux', + ], + }, + ); +}); + +test('removes multiple bindings in the same comment', (t) => { + t.deepEqual( + removeCommentedOutBindings({ + sql: 'SELECT $1\n-- $2 $3 $4\n$5', + values: [ + 'foo', + 'bar', + 'baz', + 'qux', + 'quux', + ], + }), + { + sql: 'SELECT $1 $2', + values: [ + 'foo', + 'quux', + ], + }, + ); +}); + +test('removes multiple bindings in the same comment (block comment)', (t) => { + t.deepEqual( + removeCommentedOutBindings({ + sql: 'SELECT $1 /* $2 $3 $4 */ $5', + values: [ + 'foo', + 'bar', + 'baz', + 'qux', + 'quux', + ], + }), + { + sql: 'SELECT $1 $2', + values: [ + 'foo', + 'quux', + ], + }, + ); +});