Skip to content

Commit 18d833e

Browse files
authored
Add CREATE INDEX statement parsing and EXPLAIN output (#94)
1 parent dac652f commit 18d833e

File tree

6 files changed

+129
-1011
lines changed

6 files changed

+129
-1011
lines changed

ast/ast.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,18 @@ func (s *ShowCreateQuotaQuery) Pos() token.Position { return s.Position }
832832
func (s *ShowCreateQuotaQuery) End() token.Position { return s.Position }
833833
func (s *ShowCreateQuotaQuery) statementNode() {}
834834

835+
// CreateIndexQuery represents a CREATE INDEX statement.
836+
type CreateIndexQuery struct {
837+
Position token.Position `json:"-"`
838+
IndexName string `json:"index_name"`
839+
Table string `json:"table"`
840+
Columns []Expression `json:"columns,omitempty"`
841+
}
842+
843+
func (c *CreateIndexQuery) Pos() token.Position { return c.Position }
844+
func (c *CreateIndexQuery) End() token.Position { return c.Position }
845+
func (c *CreateIndexQuery) statementNode() {}
846+
835847
// -----------------------------------------------------------------------------
836848
// Expressions
837849

internal/explain/explain.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
149149
explainTruncateQuery(sb, n, indent)
150150
case *ast.CheckQuery:
151151
explainCheckQuery(sb, n, indent)
152+
case *ast.CreateIndexQuery:
153+
explainCreateIndexQuery(sb, n, indent, depth)
152154

153155
// Types
154156
case *ast.DataType:

internal/explain/statements.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,3 +960,38 @@ func explainCheckQuery(sb *strings.Builder, n *ast.CheckQuery, indent string) {
960960
fmt.Fprintf(sb, "%s Set\n", indent)
961961
}
962962
}
963+
964+
func explainCreateIndexQuery(sb *strings.Builder, n *ast.CreateIndexQuery, indent string, depth int) {
965+
if n == nil {
966+
fmt.Fprintf(sb, "%s*ast.CreateIndexQuery\n", indent)
967+
return
968+
}
969+
970+
// CreateIndexQuery with two spaces before table name, always 3 children
971+
fmt.Fprintf(sb, "%sCreateIndexQuery %s (children %d)\n", indent, n.Table, 3)
972+
973+
// Child 1: Index name
974+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.IndexName)
975+
976+
// Child 2: Index wrapper with columns
977+
fmt.Fprintf(sb, "%s Index (children 1)\n", indent)
978+
979+
// For single column, output as Identifier
980+
// For multiple columns or if there are any special cases, output as Function tuple
981+
if len(n.Columns) == 1 {
982+
if ident, ok := n.Columns[0].(*ast.Identifier); ok {
983+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, ident.Name())
984+
} else {
985+
// Non-identifier expression - wrap in tuple
986+
fmt.Fprintf(sb, "%s Function tuple (children 1)\n", indent)
987+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
988+
}
989+
} else {
990+
// Multiple columns or empty - always Function tuple with ExpressionList
991+
fmt.Fprintf(sb, "%s Function tuple (children 1)\n", indent)
992+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
993+
}
994+
995+
// Child 3: Table name
996+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
997+
}

parser/parser.go

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,12 +1247,25 @@ func (p *Parser) parseInsert() *ast.InsertQuery {
12471247
return ins
12481248
}
12491249

1250-
func (p *Parser) parseCreate() *ast.CreateQuery {
1251-
create := &ast.CreateQuery{
1252-
Position: p.current.Pos,
1250+
func (p *Parser) parseCreate() ast.Statement {
1251+
pos := p.current.Pos
1252+
p.nextToken() // skip CREATE
1253+
1254+
// Handle CREATE [UNIQUE] INDEX
1255+
if p.currentIs(token.INDEX) {
1256+
return p.parseCreateIndex(pos)
1257+
}
1258+
// Handle CREATE UNIQUE INDEX
1259+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "UNIQUE" {
1260+
p.nextToken() // skip UNIQUE
1261+
if p.currentIs(token.INDEX) {
1262+
return p.parseCreateIndex(pos)
1263+
}
12531264
}
12541265

1255-
p.nextToken() // skip CREATE
1266+
create := &ast.CreateQuery{
1267+
Position: pos,
1268+
}
12561269

12571270
// Handle OR REPLACE
12581271
if p.currentIs(token.OR) {
@@ -1328,6 +1341,67 @@ func (p *Parser) parseCreate() *ast.CreateQuery {
13281341
return create
13291342
}
13301343

1344+
func (p *Parser) parseCreateIndex(pos token.Position) *ast.CreateIndexQuery {
1345+
p.nextToken() // skip INDEX
1346+
1347+
query := &ast.CreateIndexQuery{
1348+
Position: pos,
1349+
}
1350+
1351+
// Parse index name
1352+
query.IndexName = p.parseIdentifierName()
1353+
1354+
// Skip IF NOT EXISTS if present
1355+
if p.currentIs(token.IF) {
1356+
p.nextToken() // IF
1357+
if p.currentIs(token.NOT) {
1358+
p.nextToken() // NOT
1359+
}
1360+
if p.currentIs(token.EXISTS) {
1361+
p.nextToken() // EXISTS
1362+
}
1363+
}
1364+
1365+
// Expect ON
1366+
if p.currentIs(token.ON) {
1367+
p.nextToken()
1368+
}
1369+
1370+
// Parse table name
1371+
query.Table = p.parseIdentifierName()
1372+
if p.currentIs(token.DOT) {
1373+
p.nextToken()
1374+
query.Table = p.parseIdentifierName()
1375+
}
1376+
1377+
// Parse column list in parentheses
1378+
if p.currentIs(token.LPAREN) {
1379+
p.nextToken() // skip (
1380+
1381+
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) {
1382+
col := p.parseExpression(0)
1383+
query.Columns = append(query.Columns, col)
1384+
1385+
// Skip ASC/DESC modifiers
1386+
if p.currentIs(token.ASC) || p.currentIs(token.DESC) {
1387+
p.nextToken()
1388+
}
1389+
1390+
if p.currentIs(token.COMMA) {
1391+
p.nextToken()
1392+
} else {
1393+
break
1394+
}
1395+
}
1396+
1397+
if p.currentIs(token.RPAREN) {
1398+
p.nextToken() // skip )
1399+
}
1400+
}
1401+
1402+
return query
1403+
}
1404+
13311405
func (p *Parser) parseCreateTable(create *ast.CreateQuery) {
13321406
// Handle IF NOT EXISTS
13331407
if p.currentIs(token.IF) {

0 commit comments

Comments
 (0)