Skip to content

Commit

Permalink
address string merge hazards
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesamuel committed Feb 27, 2018
1 parent c021a92 commit 877df9f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
34 changes: 26 additions & 8 deletions lib/es6/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ function interpolateSqlIntoFragment (
const delimiter = delimiters[i - 1];
const value = values[i - 1];

if (delimiter) {
result += escapeDelimitedValue(
value, delimiter, timeZone, forbidQualified);
} else {
result += SqlString.escape(value, stringifyObjects, timeZone);
}

result += chunk;
let escaped = delimiter
? escapeDelimitedValue(value, delimiter, timeZone, forbidQualified)
: defangMergeHazard(
result,
SqlString.escape(value, stringifyObjects, timeZone),
chunk);

result += escaped + chunk;
}

return SqlString.raw(result);
Expand All @@ -95,6 +95,24 @@ function escapeDelimitedValue (value, delimiter, timeZone, forbidQualified) {
return escaped.substring(1, escaped.length - 1);
}

function defangMergeHazard (before, escaped, after) {
const escapedLast = escaped[escaped.length - 1];
if ('\"\'`'.indexOf(escapedLast) < 0) {
// Not a merge hazard.
return escaped;
}

let escapedSetOff = escaped;
const lastBefore = before[before.length - 1];
if (escapedLast === escaped[0] && escapedLast === lastBefore) {
escapedSetOff = ' ' + escapedSetOff;
}
if (escapedLast === after[0]) {
escapedSetOff += ' ';
}
return escapedSetOff;
}

/**
* Template tag function that contextually autoescapes values
* producing a SqlFragment.
Expand Down
35 changes: 32 additions & 3 deletions test/unit/es6/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,37 @@ test('template tag', {
// Ending in a comment is a concatenation hazard.
// See comments in lib/es6/Lexer.js.
assert.throws(() => sql`SELECT (${0}) -- comment`);
},
'merge-word-string': function () {
runTagTest(
`SELECT utf8'foo'`,
() => sql`SELECT utf8${'foo'}`);
},
'merge-string-string': function () {
runTagTest(
// Adjacent string tokens are concatenated, but 'a''b' is a
// 3-char string with a single-quote in the middle.
`SELECT 'a' 'b'`,
() => sql`SELECT ${'a'}${'b'}`);
},
'merge-bq-bq': function () {
runTagTest(
'SELECT `a` `b`',
() => sql`SELECT ${SqlString.identifier('a')}${SqlString.identifier('b')}`);
},
'merge-static-string-string': function () {
runTagTest(
`SELECT 'a' 'b'`,
() => sql`SELECT 'a'${'b'}`);
},
'merge-string-static-string': function () {
runTagTest(
`SELECT 'a' 'b'`,
() => sql`SELECT ${'a'}'b'`);
},
'not-a-merge-hazard': function () {
runTagTest(
`SELECT 'a''b'`,
() => sql`SELECT 'a''b'`);
}

// TODO: is there a token-merging hazard in sql`SELECT 'x'${'y'}`?
// A literal ' shows up in the decoded output of a single string literal.
});

0 comments on commit 877df9f

Please sign in to comment.