Skip to content

Commit db609ec

Browse files
authored
fix: dollar-quote tags may not contain whitespaces (#606)
Whitespaces inside dollar-quote tags are not allowed. Tags for dollar-quoted strings follow the rules for unquoted identifiers in PostgreSQL. See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
1 parent d050b1c commit db609ec

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

parser/simple_parser.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (p *simpleParser) eatDollarTag() (string, bool) {
138138
return "", false
139139
}
140140
} else {
141-
if p.eatToken('$') {
141+
if p.eatTokenWithWhitespaceOption('$' /*eatWhiteSpaces=*/, false) {
142142
return string(p.sql[startPos : p.pos-1]), true
143143
}
144144
if !p.isValidIdentifierChar() {
@@ -393,12 +393,18 @@ func (p *simpleParser) skipStatementHint() bool {
393393
// isMultibyte returns true if the character at the current position
394394
// is a multibyte utf8 character.
395395
func (p *simpleParser) isMultibyte() bool {
396+
if p.pos >= len(p.sql) {
397+
return false
398+
}
396399
return isMultibyte(p.sql[p.pos])
397400
}
398401

399402
// nextChar moves the parser to the next character. This takes into
400403
// account that some characters could be multibyte characters.
401404
func (p *simpleParser) nextChar() {
405+
if p.pos >= len(p.sql) {
406+
return
407+
}
402408
if !p.isMultibyte() {
403409
p.pos++
404410
return

parser/statement_parser_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,11 @@ SELECT * FROM PersonsTable WHERE id=$1`,
11661166
wantSQL: `select foo from bar where id=$tag$this is a string$tag$ and value=$1 order by value`,
11671167
want: []string{"p1"},
11681168
},
1169+
"dollar-quoted string with tag with unicode chars": {
1170+
input: `SELECT $ÿabc0$literal string ? ?$ÿabc0 $ÿabc0$`,
1171+
wantSQL: `SELECT $ÿabc0$literal string ? ?$ÿabc0 $ÿabc0$`,
1172+
want: []string{},
1173+
},
11691174
"invalid dollar-quoted string": {
11701175
input: "select foo from bar where id=$tag$this is an invalid string and value=? order by value",
11711176
wantErr: spanner.ToSpannerError(
@@ -1255,6 +1260,16 @@ func TestFindParamsWithCommentsPostgreSQL(t *testing.T) {
12551260
wantSQL: `$1$tag$?it$?s$tag$%s$2`,
12561261
want: []string{"p1", "p2"},
12571262
},
1263+
"dollar-quoted string with tag similar tag inside": {
1264+
input: `$tag$ $tag $tag$`,
1265+
wantSQL: `$tag$ $tag $tag$`,
1266+
want: []string{},
1267+
},
1268+
"dollar-quoted string with nested dollar-quoted string": {
1269+
input: ` ? $tag$ $tag2$ test ? $tag2$ $tag$ ? `,
1270+
wantSQL: ` $1 $tag$ $tag2$ test ? $tag2$ $tag$ $2 `,
1271+
want: []string{"p1", "p2"},
1272+
},
12581273
"dollar-quoted string with linefeed": {
12591274
input: `?%s$$?it\'?s
12601275
?it\'?s$$?`,
@@ -2294,6 +2309,26 @@ func TestEatDollarQuotedString(t *testing.T) {
22942309
want: "",
22952310
wantErr: true,
22962311
},
2312+
{
2313+
input: "$outer$ outer string $outer $outer$",
2314+
want: " outer string $outer ",
2315+
wantErr: false,
2316+
},
2317+
{
2318+
input: "$tag$value $tag#$tag$",
2319+
want: "value $tag#",
2320+
wantErr: false,
2321+
},
2322+
{
2323+
input: "$tag$value $tag--not a comment$tag$",
2324+
want: "value $tag--not a comment",
2325+
wantErr: false,
2326+
},
2327+
{
2328+
input: "$tag$value $tag/*not a comment*/$tag$",
2329+
want: "value $tag/*not a comment*/",
2330+
wantErr: false,
2331+
},
22972332
}
22982333
statementParser, err := NewStatementParser(databasepb.DatabaseDialect_POSTGRESQL, 1000)
22992334
if err != nil {

0 commit comments

Comments
 (0)