Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions obfuscator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,31 @@ func TestObfuscator(t *testing.T) {
expected: "SELECT * FROM users where id = ?",
replaceDigits: false,
},
{
// SQL Server does not treat backslash as a string escape, so
// ESCAPE N'\' is a complete literal (a single backslash).
input: `DECLARE @p1 NVARCHAR(50)=N'%foo%', @p2 NVARCHAR(50)=N'%bar%'; SELECT col FROM tbl WHERE col LIKE @p1 ESCAPE N'\' AND col LIKE @p2 ESCAPE N'\';`,
expected: `DECLARE @p1 NVARCHAR(?)=N?, @p2 NVARCHAR(?)=N?; SELECT col FROM tbl WHERE col LIKE @p1 ESCAPE N? AND col LIKE @p2 ESCAPE N?;`,
dbms: DBMSSQLServer,
},
{
// Oracle does not treat backslash as a string escape either.
input: `SELECT col FROM tbl WHERE col LIKE '%foo\_%' ESCAPE '\' AND col LIKE '%bar\_%' ESCAPE '\' AND flag = 'x'`,
expected: `SELECT col FROM tbl WHERE col LIKE ? ESCAPE ? AND col LIKE ? ESCAPE ? AND flag = ?`,
dbms: DBMSOracle,
},
{
// MySQL genuinely uses backslash as a string escape
input: `SELECT col FROM tbl WHERE col LIKE '%foo%' ESCAPE '\' AND flag = 1`,
expected: `SELECT col FROM tbl WHERE col LIKE ? ESCAPE ?`,
dbms: DBMSMySQL,
},
{
// Snowflake also supports backslash escape sequences
input: `SELECT col FROM tbl WHERE col LIKE '%foo%' ESCAPE '\' AND flag = 1`,
expected: `SELECT col FROM tbl WHERE col LIKE ? ESCAPE ?`,
dbms: DBMSSnowflake,
},
{
input: "SELECT * FROM \"users table\" where id = 1",
expected: "SELECT * FROM \"users table\" where id = ?",
Expand Down
6 changes: 5 additions & 1 deletion sqllexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ func (s *Lexer) scanStringWithDelimiter(delimiter rune) *Token {
escaped := false
escapedQuote := false

// SQL Server (T-SQL) and Oracle do not use backslash as a string escape
// character; a quote inside a literal is escaped by doubling it ('').
backslashEscapes := s.config.DBMS != DBMSSQLServer && s.config.DBMS != DBMSOracle

ch := s.next() // consume opening quote

for ; !isEOF(ch); ch = s.next() {
Expand All @@ -327,7 +331,7 @@ func (s *Lexer) scanStringWithDelimiter(delimiter rune) *Token {
continue
}

if ch == '\\' {
if backslashEscapes && ch == '\\' {
escaped = true
continue
}
Expand Down
73 changes: 73 additions & 0 deletions sqllexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,79 @@ here */`,
{STRING, `'\'`},
},
},
{
// In SQL Server, backslash is not a string escape character
// (quotes are escaped by doubling: ''). A string literal containing a
// lone backslash, e.g. ESCAPE '\', must terminate at its closing quote
// even when more SQL follows.
name: "sqlserver escape backslash mid-query",
input: `SELECT * FROM t WHERE x LIKE '%a%' ESCAPE '\' AND y = 1`,
expected: []TokenSpec{
{COMMAND, "SELECT"},
{SPACE, " "},
{WILDCARD, "*"},
{SPACE, " "},
{KEYWORD, "FROM"},
{SPACE, " "},
{IDENT, "t"},
{SPACE, " "},
{KEYWORD, "WHERE"},
{SPACE, " "},
{IDENT, "x"},
{SPACE, " "},
{KEYWORD, "LIKE"},
{SPACE, " "},
{STRING, `'%a%'`},
{SPACE, " "},
{KEYWORD, "ESCAPE"},
{SPACE, " "},
{STRING, `'\'`},
{SPACE, " "},
{KEYWORD, "AND"},
{SPACE, " "},
{IDENT, "y"},
{SPACE, " "},
{OPERATOR, "="},
{SPACE, " "},
{NUMBER, "1"},
},
lexerOpts: []lexerOption{WithDBMS(DBMSSQLServer)},
},
{
// Oracle also does not treat backslash as a string escape character.
name: "oracle escape backslash mid-query",
input: `SELECT * FROM t WHERE x LIKE '%a%' ESCAPE '\' AND y = 1`,
expected: []TokenSpec{
{COMMAND, "SELECT"},
{SPACE, " "},
{WILDCARD, "*"},
{SPACE, " "},
{KEYWORD, "FROM"},
{SPACE, " "},
{IDENT, "t"},
{SPACE, " "},
{KEYWORD, "WHERE"},
{SPACE, " "},
{IDENT, "x"},
{SPACE, " "},
{KEYWORD, "LIKE"},
{SPACE, " "},
{STRING, `'%a%'`},
{SPACE, " "},
{KEYWORD, "ESCAPE"},
{SPACE, " "},
{STRING, `'\'`},
{SPACE, " "},
{KEYWORD, "AND"},
{SPACE, " "},
{IDENT, "y"},
{SPACE, " "},
{OPERATOR, "="},
{SPACE, " "},
{NUMBER, "1"},
},
lexerOpts: []lexerOption{WithDBMS(DBMSOracle)},
},
{
name: "simple select with multiline comments as separators",
input: `SELECT/**/*/**/FROM/**/test`,
Expand Down
Loading