Skip to content

Commit 3c0c798

Browse files
committed
Add SHOW INDEX parsing and escape single quotes in identifiers
- Add parsing for SHOW INDEX/INDEXES/INDICES/KEYS statements Maps to ShowColumns type as ClickHouse does internally - Handle SHOW EXTENDED INDEX syntax - Handle SHOW INDEX FROM table FROM database syntax - Add EscapeIdentifier function to escape single quotes as \' - Apply escaping to DropQuery and CreateQuery output Fixes 02724_show_indexes and related tests.
1 parent 5bff0fe commit 3c0c798

File tree

5 files changed

+48
-43
lines changed

5 files changed

+48
-43
lines changed

internal/explain/format.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ func FormatFloat(val float64) string {
3737
return strconv.FormatFloat(val, 'f', -1, 64)
3838
}
3939

40+
// EscapeIdentifier escapes single quotes in identifiers for EXPLAIN AST output
41+
// ClickHouse escapes ' as \' in identifier names
42+
func EscapeIdentifier(s string) string {
43+
return strings.ReplaceAll(s, "'", "\\'")
44+
}
45+
4046
// escapeStringLiteral escapes special characters in a string for EXPLAIN AST output
4147
// Uses double-escaping as ClickHouse EXPLAIN AST displays strings
4248
// Iterates over bytes to preserve raw bytes (including invalid UTF-8)

internal/explain/statements.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,16 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
179179
}
180180
// ClickHouse adds an extra space before (children N) for CREATE DATABASE
181181
if n.CreateDatabase {
182-
fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, name, children)
183-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
182+
fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, EscapeIdentifier(name), children)
183+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
184184
} else if hasDatabase {
185185
// Database-qualified: CreateQuery db table (children N)
186-
fmt.Fprintf(sb, "%sCreateQuery %s %s (children %d)\n", indent, n.Database, name, children)
187-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
188-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
186+
fmt.Fprintf(sb, "%sCreateQuery %s %s (children %d)\n", indent, EscapeIdentifier(n.Database), EscapeIdentifier(name), children)
187+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(n.Database))
188+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
189189
} else {
190-
fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, name, children)
191-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
190+
fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, EscapeIdentifier(name), children)
191+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
192192
}
193193
if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Projections) > 0 || len(n.Constraints) > 0 {
194194
childrenCount := 0
@@ -492,16 +492,16 @@ func explainDropQuery(sb *strings.Builder, n *ast.DropQuery, indent string, dept
492492
hasDatabase := n.Database != "" && !n.DropDatabase
493493
if hasDatabase {
494494
// Database-qualified: DropQuery db table (children 2)
495-
fmt.Fprintf(sb, "%sDropQuery %s %s (children %d)\n", indent, n.Database, name, 2)
496-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
497-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
495+
fmt.Fprintf(sb, "%sDropQuery %s %s (children %d)\n", indent, EscapeIdentifier(n.Database), EscapeIdentifier(name), 2)
496+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(n.Database))
497+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
498498
} else if n.DropDatabase {
499499
// DROP DATABASE uses different spacing
500-
fmt.Fprintf(sb, "%sDropQuery %s (children %d)\n", indent, name, 1)
501-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
500+
fmt.Fprintf(sb, "%sDropQuery %s (children %d)\n", indent, EscapeIdentifier(name), 1)
501+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
502502
} else {
503-
fmt.Fprintf(sb, "%sDropQuery %s (children %d)\n", indent, name, 1)
504-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
503+
fmt.Fprintf(sb, "%sDropQuery %s (children %d)\n", indent, EscapeIdentifier(name), 1)
504+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, EscapeIdentifier(name))
505505
}
506506
}
507507

parser/parser.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4549,6 +4549,10 @@ func (p *Parser) parseShow() ast.Statement {
45494549
case token.COLUMNS:
45504550
show.ShowType = ast.ShowColumns
45514551
p.nextToken()
4552+
case token.INDEX:
4553+
// SHOW INDEX FROM table - treat as ShowColumns (ClickHouse maps to ShowColumns)
4554+
show.ShowType = ast.ShowColumns
4555+
p.nextToken()
45524556
case token.CREATE:
45534557
p.nextToken()
45544558
if p.currentIs(token.DATABASE) {
@@ -4625,11 +4629,25 @@ func (p *Parser) parseShow() ast.Statement {
46254629
show.ShowType = ast.ShowDictionaries
46264630
case "FUNCTIONS":
46274631
show.ShowType = ast.ShowFunctions
4632+
case "INDEXES", "INDICES", "KEYS":
4633+
// SHOW INDEXES/INDICES/KEYS FROM table - treat as ShowColumns
4634+
show.ShowType = ast.ShowColumns
4635+
case "EXTENDED":
4636+
// SHOW EXTENDED INDEX FROM table - treat as ShowColumns
4637+
p.nextToken()
4638+
if p.currentIs(token.INDEX) {
4639+
p.nextToken()
4640+
}
4641+
show.ShowType = ast.ShowColumns
4642+
// Don't consume another token, fall through to FROM parsing
4643+
goto parseFrom
46284644
}
46294645
p.nextToken()
46304646
}
46314647
}
46324648

4649+
parseFrom:
4650+
46334651
// Parse FROM clause (or table/database name for SHOW CREATE TABLE/DATABASE/DICTIONARY/VIEW)
46344652
showCreateTypes := show.ShowType == ast.ShowCreate || show.ShowType == ast.ShowCreateDB || show.ShowType == ast.ShowCreateDictionary || show.ShowType == ast.ShowCreateView
46354653
if p.currentIs(token.FROM) || (showCreateTypes && (p.currentIs(token.IDENT) || p.current.Token.IsKeyword())) {
@@ -4653,6 +4671,15 @@ func (p *Parser) parseShow() ast.Statement {
46534671
}
46544672
}
46554673

4674+
// Handle SHOW INDEX FROM table FROM database syntax (second FROM for database)
4675+
if p.currentIs(token.FROM) && show.ShowType == ast.ShowColumns {
4676+
p.nextToken()
4677+
if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() {
4678+
show.Database = p.current.Value
4679+
p.nextToken()
4680+
}
4681+
}
4682+
46564683
// Parse NOT LIKE, LIKE or ILIKE clause
46574684
if p.currentIs(token.NOT) {
46584685
p.nextToken()
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
{
22
"explain_todo": {
33
"stmt2": true,
4-
"stmt35": true,
5-
"stmt36": true,
6-
"stmt37": true,
7-
"stmt40": true,
8-
"stmt41": true,
94
"stmt5": true,
10-
"stmt7": true,
115
"stmt9": true
126
}
137
}
Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt11": true,
4-
"stmt15": true,
5-
"stmt19": true,
6-
"stmt23": true,
7-
"stmt25": true,
8-
"stmt26": true,
9-
"stmt27": true,
10-
"stmt28": true,
11-
"stmt29": true,
12-
"stmt30": true,
13-
"stmt31": true,
14-
"stmt37": true,
15-
"stmt39": true,
16-
"stmt4": true,
17-
"stmt41": true,
18-
"stmt5": true,
19-
"stmt6": true,
20-
"stmt7": true,
21-
"stmt9": true
22-
}
23-
}
1+
{}

0 commit comments

Comments
 (0)