@@ -71,14 +71,14 @@ function interpolateSqlIntoFragment (
7171 const delimiter = delimiters [ i - 1 ] ;
7272 const value = values [ i - 1 ] ;
7373
74- if ( delimiter ) {
75- result += escapeDelimitedValue (
76- value , delimiter , timeZone , forbidQualified ) ;
77- } else {
78- result += SqlString . escape ( value , stringifyObjects , timeZone ) ;
79- }
80-
81- result += chunk ;
74+ let escaped = delimiter
75+ ? escapeDelimitedValue ( value , delimiter , timeZone , forbidQualified )
76+ : defangMergeHazard (
77+ result ,
78+ SqlString . escape ( value , stringifyObjects , timeZone ) ,
79+ chunk ) ;
80+
81+ result += escaped + chunk ;
8282 }
8383
8484 return SqlString . raw ( result ) ;
@@ -95,6 +95,24 @@ function escapeDelimitedValue (value, delimiter, timeZone, forbidQualified) {
9595 return escaped . substring ( 1 , escaped . length - 1 ) ;
9696}
9797
98+ function defangMergeHazard ( before , escaped , after ) {
99+ const escapedLast = escaped [ escaped . length - 1 ] ;
100+ if ( '\"\'`' . indexOf ( escapedLast ) < 0 ) {
101+ // Not a merge hazard.
102+ return escaped ;
103+ }
104+
105+ let escapedSetOff = escaped ;
106+ const lastBefore = before [ before . length - 1 ] ;
107+ if ( escapedLast === escaped [ 0 ] && escapedLast === lastBefore ) {
108+ escapedSetOff = ' ' + escapedSetOff ;
109+ }
110+ if ( escapedLast === after [ 0 ] ) {
111+ escapedSetOff += ' ' ;
112+ }
113+ return escapedSetOff ;
114+ }
115+
98116/**
99117 * Template tag function that contextually autoescapes values
100118 * producing a SqlFragment.
0 commit comments