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
1 change: 1 addition & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@ type CaseExpr struct {
Whens []*WhenClause `json:"whens"`
Else Expression `json:"else,omitempty"`
Alias string `json:"alias,omitempty"`
QuotedAlias bool `json:"quoted_alias,omitempty"` // true if alias was double-quoted
}

func (c *CaseExpr) Pos() token.Position { return c.Position }
Expand Down
2 changes: 2 additions & 0 deletions internal/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
explainWithElement(sb, n, indent, depth)
case *ast.Asterisk:
explainAsterisk(sb, n, indent)
case *ast.ColumnsMatcher:
fmt.Fprintf(sb, "%sColumnsRegexpMatcher\n", indent)

// Functions
case *ast.FunctionCall:
Expand Down
2 changes: 1 addition & 1 deletion internal/explain/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func escapeStringLiteral(s string) string {
case '\\':
sb.WriteString("\\\\\\\\") // backslash becomes four backslashes (\\\\)
case '\'':
sb.WriteString("\\\\\\'") // single quote becomes \\\' (escaped backslash + escaped quote)
sb.WriteString("\\\\\\'") // single quote becomes \\\' (three backslashes + quote)
case '\n':
sb.WriteString("\\\\n") // newline becomes \\n
case '\t':
Expand Down
13 changes: 12 additions & 1 deletion internal/explain/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ func explainCastExprWithAlias(sb *strings.Builder, n *ast.CastExpr, alias string
Node(sb, n.TypeExpr, depth+2)
} else {
typeStr := FormatDataType(n.Type)
// Only escape if the DataType doesn't have parameters - this means the entire
// type was parsed from a string literal and may contain unescaped quotes.
// If it has parameters, FormatDataType already handles escaping.
if n.Type == nil || len(n.Type.Parameters) == 0 {
typeStr = escapeStringLiteral(typeStr)
}
fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, typeStr)
}
}
Expand Down Expand Up @@ -711,7 +717,12 @@ func explainIsNullExpr(sb *strings.Builder, n *ast.IsNullExpr, indent string, de
}

func explainCaseExpr(sb *strings.Builder, n *ast.CaseExpr, indent string, depth int) {
explainCaseExprWithAlias(sb, n, "", indent, depth)
// Only output alias if it's unquoted (ClickHouse doesn't show quoted aliases)
alias := ""
if n.Alias != "" && !n.QuotedAlias {
alias = n.Alias
}
explainCaseExprWithAlias(sb, n, alias, indent, depth)
}

func explainCaseExprWithAlias(sb *strings.Builder, n *ast.CaseExpr, alias string, indent string, depth int) {
Expand Down
11 changes: 6 additions & 5 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ type Lexer struct {

// Item represents a lexical token with its value and position.
type Item struct {
Token token.Token
Value string
Pos token.Position
Token token.Token
Value string
Pos token.Position
Quoted bool // true if this identifier was double-quoted
}

// New creates a new Lexer from an io.Reader.
Expand Down Expand Up @@ -453,7 +454,7 @@ func (l *Lexer) readQuotedIdentifier() Item {
sb.WriteRune(l.ch)
l.readChar()
}
return Item{Token: token.IDENT, Value: sb.String(), Pos: pos}
return Item{Token: token.IDENT, Value: sb.String(), Pos: pos, Quoted: true}
}

// readUnicodeString reads a string enclosed in Unicode curly quotes (' or ')
Expand Down Expand Up @@ -497,7 +498,7 @@ func (l *Lexer) readUnicodeQuotedIdentifier(openQuote rune) Item {
if l.ch == closeQuote {
l.readChar() // skip closing quote
}
return Item{Token: token.IDENT, Value: sb.String(), Pos: pos}
return Item{Token: token.IDENT, Value: sb.String(), Pos: pos, Quoted: true}
}

func (l *Lexer) readBacktickIdentifier() Item {
Expand Down
1 change: 1 addition & 0 deletions parser/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ func (p *Parser) parseCase() ast.Expression {
p.nextToken()
if p.currentIs(token.IDENT) {
expr.Alias = p.current.Value
expr.QuotedAlias = p.current.Quoted
p.nextToken()
}
}
Expand Down
2 changes: 1 addition & 1 deletion parser/testdata/00551_parse_or_null/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@
"value": 555555
}
},
"alias": "LONG_COL_0"
"alias": "LONG_COL_0",
"quoted_alias": true
}
],
"from": {
Expand Down
2 changes: 1 addition & 1 deletion parser/testdata/02910_nullable_enum_cast/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}