Skip to content

Commit 5d2f760

Browse files
authored
Add query handlers for GRANT, SHOW GRANTS, EXISTS variants, and SHOW CREATE variants (#89)
1 parent cfc44a7 commit 5d2f760

File tree

15 files changed

+180
-173
lines changed

15 files changed

+180
-173
lines changed

ast/ast.go

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -652,15 +652,17 @@ func (s *ShowQuery) statementNode() {}
652652
type ShowType string
653653

654654
const (
655-
ShowTables ShowType = "TABLES"
656-
ShowDatabases ShowType = "DATABASES"
657-
ShowProcesses ShowType = "PROCESSLIST"
658-
ShowCreate ShowType = "CREATE"
659-
ShowCreateDB ShowType = "CREATE_DATABASE"
660-
ShowColumns ShowType = "COLUMNS"
661-
ShowDictionaries ShowType = "DICTIONARIES"
662-
ShowFunctions ShowType = "FUNCTIONS"
663-
ShowSettings ShowType = "SETTINGS"
655+
ShowTables ShowType = "TABLES"
656+
ShowDatabases ShowType = "DATABASES"
657+
ShowProcesses ShowType = "PROCESSLIST"
658+
ShowCreate ShowType = "CREATE"
659+
ShowCreateDB ShowType = "CREATE_DATABASE"
660+
ShowCreateDictionary ShowType = "CREATE_DICTIONARY"
661+
ShowCreateView ShowType = "CREATE_VIEW"
662+
ShowColumns ShowType = "COLUMNS"
663+
ShowDictionaries ShowType = "DICTIONARIES"
664+
ShowFunctions ShowType = "FUNCTIONS"
665+
ShowSettings ShowType = "SETTINGS"
664666
)
665667

666668
// ExplainQuery represents an EXPLAIN statement.
@@ -770,17 +772,47 @@ func (e *ExchangeQuery) Pos() token.Position { return e.Position }
770772
func (e *ExchangeQuery) End() token.Position { return e.Position }
771773
func (e *ExchangeQuery) statementNode() {}
772774

775+
// ExistsType represents the type of EXISTS query.
776+
type ExistsType string
777+
778+
const (
779+
ExistsTable ExistsType = "TABLE"
780+
ExistsDictionary ExistsType = "DICTIONARY"
781+
ExistsDatabase ExistsType = "DATABASE"
782+
ExistsView ExistsType = "VIEW"
783+
)
784+
773785
// ExistsQuery represents an EXISTS table_name statement (check if table exists).
774786
type ExistsQuery struct {
775-
Position token.Position `json:"-"`
776-
Database string `json:"database,omitempty"`
777-
Table string `json:"table"`
787+
Position token.Position `json:"-"`
788+
ExistsType ExistsType `json:"exists_type,omitempty"`
789+
Database string `json:"database,omitempty"`
790+
Table string `json:"table"`
778791
}
779792

780793
func (e *ExistsQuery) Pos() token.Position { return e.Position }
781794
func (e *ExistsQuery) End() token.Position { return e.Position }
782795
func (e *ExistsQuery) statementNode() {}
783796

797+
// GrantQuery represents a GRANT or REVOKE statement.
798+
type GrantQuery struct {
799+
Position token.Position `json:"-"`
800+
IsRevoke bool `json:"is_revoke,omitempty"`
801+
}
802+
803+
func (g *GrantQuery) Pos() token.Position { return g.Position }
804+
func (g *GrantQuery) End() token.Position { return g.Position }
805+
func (g *GrantQuery) statementNode() {}
806+
807+
// ShowGrantsQuery represents a SHOW GRANTS statement.
808+
type ShowGrantsQuery struct {
809+
Position token.Position `json:"-"`
810+
}
811+
812+
func (s *ShowGrantsQuery) Pos() token.Position { return s.Position }
813+
func (s *ShowGrantsQuery) End() token.Position { return s.Position }
814+
func (s *ShowGrantsQuery) statementNode() {}
815+
784816
// ShowPrivilegesQuery represents a SHOW PRIVILEGES statement.
785817
type ShowPrivilegesQuery struct {
786818
Position token.Position `json:"-"`

internal/explain/explain.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
127127
fmt.Fprintf(sb, "%sShowPrivilegesQuery\n", indent)
128128
case *ast.ShowCreateQuotaQuery:
129129
fmt.Fprintf(sb, "%sSHOW CREATE QUOTA query\n", indent)
130+
case *ast.ShowGrantsQuery:
131+
fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent)
132+
case *ast.GrantQuery:
133+
fmt.Fprintf(sb, "%sGrantQuery\n", indent)
130134
case *ast.UseQuery:
131135
explainUseQuery(sb, n, indent)
132136
case *ast.DescribeQuery:

internal/explain/statements.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,38 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) {
482482
return
483483
}
484484

485+
// SHOW CREATE DICTIONARY has special output format
486+
if n.ShowType == ast.ShowCreateDictionary && (n.Database != "" || n.From != "") {
487+
if n.Database != "" && n.From != "" {
488+
fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s %s (children 2)\n", indent, n.Database, n.From)
489+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
490+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
491+
} else if n.From != "" {
492+
fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children 1)\n", indent, n.From)
493+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
494+
} else if n.Database != "" {
495+
fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children 1)\n", indent, n.Database)
496+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
497+
}
498+
return
499+
}
500+
501+
// SHOW CREATE VIEW has special output format
502+
if n.ShowType == ast.ShowCreateView && (n.Database != "" || n.From != "") {
503+
if n.Database != "" && n.From != "" {
504+
fmt.Fprintf(sb, "%sShowCreateViewQuery %s %s (children 2)\n", indent, n.Database, n.From)
505+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
506+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
507+
} else if n.From != "" {
508+
fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children 1)\n", indent, n.From)
509+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
510+
} else if n.Database != "" {
511+
fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children 1)\n", indent, n.Database)
512+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
513+
}
514+
return
515+
}
516+
485517
// SHOW CREATE TABLE has special output format with database and table identifiers
486518
if n.ShowType == ast.ShowCreate && (n.Database != "" || n.From != "") {
487519
// Format: ShowCreateTableQuery database table (children 2)
@@ -550,12 +582,33 @@ func explainDescribeQuery(sb *strings.Builder, n *ast.DescribeQuery, indent stri
550582
}
551583

552584
func explainExistsTableQuery(sb *strings.Builder, n *ast.ExistsQuery, indent string) {
553-
// EXISTS TABLE/DATABASE/DICTIONARY query
585+
// Determine query type name based on ExistsType
586+
queryType := "ExistsTableQuery"
587+
switch n.ExistsType {
588+
case ast.ExistsDictionary:
589+
queryType = "ExistsDictionaryQuery"
590+
case ast.ExistsDatabase:
591+
queryType = "ExistsDatabaseQuery"
592+
case ast.ExistsView:
593+
queryType = "ExistsViewQuery"
594+
}
595+
596+
// EXISTS DATABASE has only one child (the database name stored in Table)
597+
if n.ExistsType == ast.ExistsDatabase {
598+
name := n.Table
599+
fmt.Fprintf(sb, "%s%s %s (children %d)\n", indent, queryType, name, 1)
600+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
601+
return
602+
}
603+
604+
// For TABLE/DICTIONARY/VIEW, show database and object name
554605
name := n.Table
606+
children := 1
555607
if n.Database != "" {
556608
name = n.Database + " " + n.Table
609+
children = 2
557610
}
558-
fmt.Fprintf(sb, "%sExistsTableQuery %s (children %d)\n", indent, name, 2)
611+
fmt.Fprintf(sb, "%s%s %s (children %d)\n", indent, queryType, name, children)
559612
if n.Database != "" {
560613
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
561614
}

parser/parser.go

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ func (p *Parser) parseStatement() ast.Statement {
155155
return p.parseAttach()
156156
case token.CHECK:
157157
return p.parseCheck()
158+
case token.GRANT:
159+
return p.parseGrant()
160+
case token.REVOKE:
161+
return p.parseRevoke()
158162
default:
159163
p.errors = append(p.errors, fmt.Errorf("unexpected token %s at line %d, column %d",
160164
p.current.Token, p.current.Pos.Line, p.current.Pos.Column))
@@ -3188,6 +3192,15 @@ func (p *Parser) parseShow() ast.Statement {
31883192
return &ast.ShowPrivilegesQuery{Position: pos}
31893193
}
31903194

3195+
// Handle SHOW GRANTS - it has its own statement type
3196+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "GRANTS" {
3197+
// Skip all remaining tokens until end of statement
3198+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
3199+
p.nextToken()
3200+
}
3201+
return &ast.ShowGrantsQuery{Position: pos}
3202+
}
3203+
31913204
show := &ast.ShowQuery{
31923205
Position: pos,
31933206
}
@@ -3216,6 +3229,12 @@ func (p *Parser) parseShow() ast.Statement {
32163229
p.nextToken()
32173230
}
32183231
return &ast.ShowCreateQuotaQuery{Position: pos, Name: name}
3232+
} else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" {
3233+
show.ShowType = ast.ShowCreateDictionary
3234+
p.nextToken()
3235+
} else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "VIEW" {
3236+
show.ShowType = ast.ShowCreateView
3237+
p.nextToken()
32193238
} else {
32203239
show.ShowType = ast.ShowCreate
32213240
// Handle SHOW CREATE TABLE, etc.
@@ -3242,8 +3261,9 @@ func (p *Parser) parseShow() ast.Statement {
32423261
}
32433262
}
32443263

3245-
// Parse FROM clause (or table/database name for SHOW CREATE TABLE/DATABASE)
3246-
if p.currentIs(token.FROM) || ((show.ShowType == ast.ShowCreate || show.ShowType == ast.ShowCreateDB) && (p.currentIs(token.IDENT) || p.current.Token.IsKeyword())) {
3264+
// Parse FROM clause (or table/database name for SHOW CREATE TABLE/DATABASE/DICTIONARY/VIEW)
3265+
showCreateTypes := show.ShowType == ast.ShowCreate || show.ShowType == ast.ShowCreateDB || show.ShowType == ast.ShowCreateDictionary || show.ShowType == ast.ShowCreateView
3266+
if p.currentIs(token.FROM) || (showCreateTypes && (p.currentIs(token.IDENT) || p.current.Token.IsKeyword())) {
32473267
if p.currentIs(token.FROM) {
32483268
p.nextToken()
32493269
}
@@ -3921,25 +3941,36 @@ func (p *Parser) parseParenthesizedSelect() *ast.SelectWithUnionQuery {
39213941
// parseExistsStatement handles EXISTS table_name syntax
39223942
func (p *Parser) parseExistsStatement() *ast.ExistsQuery {
39233943
exists := &ast.ExistsQuery{
3924-
Position: p.current.Pos,
3944+
Position: p.current.Pos,
3945+
ExistsType: ast.ExistsTable, // default to TABLE
39253946
}
39263947

39273948
p.nextToken() // skip EXISTS
39283949

3929-
// Skip optional TABLE keyword
3950+
// Check for DICTIONARY, DATABASE, VIEW, or TABLE keyword
39303951
if p.currentIs(token.TABLE) {
3952+
exists.ExistsType = ast.ExistsTable
3953+
p.nextToken()
3954+
} else if p.currentIs(token.DATABASE) {
3955+
exists.ExistsType = ast.ExistsDatabase
3956+
p.nextToken()
3957+
} else if p.currentIs(token.VIEW) {
3958+
exists.ExistsType = ast.ExistsView
3959+
p.nextToken()
3960+
} else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" {
3961+
exists.ExistsType = ast.ExistsDictionary
39313962
p.nextToken()
39323963
}
39333964

3934-
// Parse table name (database.table or just table)
3935-
tableName := p.parseIdentifierName()
3936-
if tableName != "" {
3965+
// Parse table/database/dictionary/view name
3966+
name := p.parseIdentifierName()
3967+
if name != "" {
39373968
if p.currentIs(token.DOT) {
39383969
p.nextToken()
3939-
exists.Database = tableName
3970+
exists.Database = name
39403971
exists.Table = p.parseIdentifierName()
39413972
} else {
3942-
exists.Table = tableName
3973+
exists.Table = name
39433974
}
39443975
}
39453976

@@ -4034,3 +4065,33 @@ func (p *Parser) parseProjection() *ast.Projection {
40344065

40354066
return proj
40364067
}
4068+
4069+
// parseGrant handles GRANT statements
4070+
func (p *Parser) parseGrant() *ast.GrantQuery {
4071+
grant := &ast.GrantQuery{
4072+
Position: p.current.Pos,
4073+
IsRevoke: false,
4074+
}
4075+
4076+
// Skip all tokens until end of statement
4077+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
4078+
p.nextToken()
4079+
}
4080+
4081+
return grant
4082+
}
4083+
4084+
// parseRevoke handles REVOKE statements
4085+
func (p *Parser) parseRevoke() *ast.GrantQuery {
4086+
grant := &ast.GrantQuery{
4087+
Position: p.current.Pos,
4088+
IsRevoke: true,
4089+
}
4090+
4091+
// Skip all tokens until end of statement
4092+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
4093+
p.nextToken()
4094+
}
4095+
4096+
return grant
4097+
}

parser/testdata/01018_ddl_dictionaries_create/metadata.json

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
{
22
"explain_todo": {
33
"stmt11": true,
4-
"stmt12": true,
54
"stmt13": true,
6-
"stmt14": true,
75
"stmt17": true,
86
"stmt18": true,
9-
"stmt19": true,
107
"stmt22": true,
118
"stmt23": true,
12-
"stmt24": true,
139
"stmt28": true,
14-
"stmt29": true,
1510
"stmt35": true,
16-
"stmt36": true,
1711
"stmt37": true,
18-
"stmt38": true,
1912
"stmt43": true,
2013
"stmt47": true,
2114
"stmt48": true,
Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,11 @@
11
{
22
"explain_todo": {
3-
"stmt11": true,
43
"stmt12": true,
5-
"stmt15": true,
6-
"stmt19": true,
74
"stmt22": true,
85
"stmt23": true,
96
"stmt24": true,
107
"stmt25": true,
11-
"stmt28": true,
12-
"stmt3": true,
13-
"stmt33": true,
148
"stmt34": true,
15-
"stmt35": true,
16-
"stmt36": true,
17-
"stmt37": true,
18-
"stmt38": true,
19-
"stmt40": true,
20-
"stmt41": true,
21-
"stmt42": true,
22-
"stmt43": true,
23-
"stmt48": true,
24-
"stmt5": true,
25-
"stmt7": true
9+
"stmt35": true
2610
}
2711
}

0 commit comments

Comments
 (0)