Skip to content

Commit 865e8c6

Browse files
Ajit Pratap SinghAjit Pratap Singh
authored andcommitted
fix(parser): escape embedded delimiters and fix empty-IN error order
- renderQuotedIdent now doubles embedded `]`, `"`, and `` ` `` per dialect convention so identifiers like [foo]bar] round-trip unambiguously. - Empty PIVOT/UNPIVOT IN list check now runs before the closing-`)` check so the user-facing error is "at least one value/column..." instead of the misleading ") to close ... IN list". - Clarify renderQuotedIdent comment to reference Token.Quote and Word.QuoteStyle as the actual sources.
1 parent ec3f4d4 commit 865e8c6

1 file changed

Lines changed: 12 additions & 10 deletions

File tree

pkg/sql/parser/pivot.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@ import (
2727

2828
// renderQuotedIdent reproduces the original delimiters of a quoted identifier
2929
// token so the parsed value round-trips through the formatter. The tokenizer
30-
// strips delimiters but records them in QuoteStyle.
30+
// strips delimiters but records the style in Token.Quote (or, for word
31+
// tokens, Word.QuoteStyle). Embedded delimiters are escaped per dialect:
32+
// SQL Server doubles `]`, ANSI doubles `"`, MySQL doubles “ ` “.
3133
func renderQuotedIdent(tok models.Token) string {
3234
q := tok.Quote
3335
if q == 0 && tok.Word != nil {
3436
q = tok.Word.QuoteStyle
3537
}
3638
switch q {
3739
case '[':
38-
return "[" + tok.Value + "]"
40+
return "[" + strings.ReplaceAll(tok.Value, "]", "]]") + "]"
3941
case '"':
40-
return "\"" + tok.Value + "\""
42+
return "\"" + strings.ReplaceAll(tok.Value, "\"", "\"\"") + "\""
4143
case '`':
42-
return "`" + tok.Value + "`"
44+
return "`" + strings.ReplaceAll(tok.Value, "`", "``") + "`"
4345
}
4446
return tok.Value
4547
}
@@ -151,12 +153,12 @@ func (p *Parser) parsePivotClause() (*ast.PivotClause, error) {
151153
}
152154
}
153155

154-
if !p.isType(models.TokenTypeRParen) {
155-
return nil, p.expectedError(") to close PIVOT IN list")
156-
}
157156
if len(inValues) == 0 {
158157
return nil, p.expectedError("at least one value in PIVOT IN list")
159158
}
159+
if !p.isType(models.TokenTypeRParen) {
160+
return nil, p.expectedError(") to close PIVOT IN list")
161+
}
160162
p.advance() // close IN list )
161163

162164
if !p.isType(models.TokenTypeRParen) {
@@ -228,12 +230,12 @@ func (p *Parser) parseUnpivotClause() (*ast.UnpivotClause, error) {
228230
}
229231
}
230232

231-
if !p.isType(models.TokenTypeRParen) {
232-
return nil, p.expectedError(") to close UNPIVOT IN list")
233-
}
234233
if len(cols) == 0 {
235234
return nil, p.expectedError("at least one column in UNPIVOT IN list")
236235
}
236+
if !p.isType(models.TokenTypeRParen) {
237+
return nil, p.expectedError(") to close UNPIVOT IN list")
238+
}
237239
p.advance() // close IN list )
238240

239241
if !p.isType(models.TokenTypeRParen) {

0 commit comments

Comments
 (0)