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
4 changes: 2 additions & 2 deletions parser/statement_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,8 +458,8 @@ func (p *StatementParser) skipQuoted(sql []byte, pos int, quote byte) (int, int,
// This was the end quote.
return pos + 1, quoteLength, nil
}
} else if (p.supportsBackslashEscape() || isEscapeString) && len(sql) > pos+1 && c == '\\' && sql[pos+1] == quote {
// This is an escaped quote (e.g. 'foo\'bar').
} else if (p.supportsBackslashEscape() || isEscapeString) && len(sql) > pos+1 && c == '\\' && (sql[pos+1] == quote || sql[pos+1] == '\\') {
// This is an escaped quote (e.g. 'foo\'bar') or an escaped backslash (e.g 'test\\').
// Note that in raw strings, the \ officially does not start an
// escape sequence, but the result is still the same, as in a raw
// string 'both characters are preserved'.
Expand Down
38 changes: 38 additions & 0 deletions parser/statement_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,17 @@ SELECT * FROM PersonsTable WHERE id=@id`,
databasepb.DatabaseDialect_POSTGRESQL: spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "SQL statement contains an unclosed literal: %s", `?"?it\"?s"?`)),
},
},
"backslash at end of string": {
input: `?'test\\'?`,
wantSQL: map[databasepb.DatabaseDialect]string{
databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL: `@p1'test\\'@p2`,
databasepb.DatabaseDialect_POSTGRESQL: `$1'test\\'$2`,
},
want: map[databasepb.DatabaseDialect][]string{
databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL: {"p1", "p2"},
databasepb.DatabaseDialect_POSTGRESQL: {"p1", "p2"},
},
},
"triple-quoted string": {
input: `?'''?it\'?s'''?`,
wantSQL: map[databasepb.DatabaseDialect]string{
Expand Down Expand Up @@ -1092,6 +1103,16 @@ SELECT * FROM PersonsTable WHERE id=$1`,
input: `?"?it\"?s"?`,
wantErr: spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "SQL statement contains an unclosed literal: %s", `?"?it\"?s"?`)),
},
"backslash at end of string": {
input: `?'test\\'?`,
wantSQL: `$1'test\\'$2`,
want: []string{"p1", "p2"},
},
"backslash at end of double-quoted string": {
input: `?"test\\"?`,
wantSQL: `$1"test\\"$2`,
want: []string{"p1", "p2"},
},
"triple-quoted string": {
input: `?'''?it\'?s'''?`,
wantErr: spanner.ToSpannerError(status.Errorf(codes.InvalidArgument, "SQL statement contains an unclosed literal: %s", `?'''?it\'?s'''?`)),
Expand Down Expand Up @@ -1263,6 +1284,16 @@ func TestFindParamsWithCommentsPostgreSQL(t *testing.T) {
wantSQL: `$1\"?it\\"\"?s\"%s$2`,
want: []string{"p1", "p2"},
},
"backslash at end of string": {
input: `?'test\\'%s?`,
wantSQL: `$1'test\\'%s$2`,
want: []string{"p1", "p2"},
},
"backslash at end of double-quoted string": {
input: `?"test\\"%s?`,
wantSQL: `$1"test\\"%s$2`,
want: []string{"p1", "p2"},
},
"triple-quotes": {
input: `?%s'''?it\''?s'''?`,
wantSQL: `$1%s'''?it\''?s'''$2`,
Expand Down Expand Up @@ -2028,6 +2059,13 @@ func TestSkip(t *testing.T) {
databasepb.DatabaseDialect_POSTGRESQL: "'''foo\\'",
},
},
"escaped backslash at end of string literal": {
input: "'test\\\\' as foo",
skipped: map[databasepb.DatabaseDialect]string{
databasepb.DatabaseDialect_GOOGLE_STANDARD_SQL: "'test\\\\'",
databasepb.DatabaseDialect_POSTGRESQL: "'test\\\\'",
},
},
"string with linefeed": {
input: "'foo\n' ",
skipped: map[databasepb.DatabaseDialect]string{
Expand Down
Loading