Skip to content

Commit 443aba5

Browse files
committed
Add CREATE/ALTER/DROP/SHOW CREATE ROW POLICY support
Implement parsing and explain output for ROW POLICY statements: - CREATE ROW POLICY / CREATE POLICY - ALTER ROW POLICY / ALTER POLICY - DROP ROW POLICY / DROP POLICY - SHOW CREATE ROW POLICY / SHOW CREATE POLICY The explain output matches ClickHouse's format: - CREATE/ALTER -> "CREATE ROW POLICY or ALTER ROW POLICY query" - DROP -> "DROP ROW POLICY query" - SHOW CREATE -> "SHOW CREATE ROW POLICY query" This fixes 57 of 60 failing statements in 01295_create_row_policy test. The remaining 3 failures are for ROLE statements (not ROW POLICY).
1 parent c74d6c6 commit 443aba5

40 files changed

Lines changed: 187 additions & 317 deletions

File tree

ast/ast.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,35 @@ func (s *ShowCreateSettingsProfileQuery) Pos() token.Position { return s.Positio
901901
func (s *ShowCreateSettingsProfileQuery) End() token.Position { return s.Position }
902902
func (s *ShowCreateSettingsProfileQuery) statementNode() {}
903903

904+
// CreateRowPolicyQuery represents a CREATE ROW POLICY or ALTER ROW POLICY statement.
905+
type CreateRowPolicyQuery struct {
906+
Position token.Position `json:"-"`
907+
IsAlter bool `json:"is_alter,omitempty"`
908+
}
909+
910+
func (c *CreateRowPolicyQuery) Pos() token.Position { return c.Position }
911+
func (c *CreateRowPolicyQuery) End() token.Position { return c.Position }
912+
func (c *CreateRowPolicyQuery) statementNode() {}
913+
914+
// DropRowPolicyQuery represents a DROP ROW POLICY statement.
915+
type DropRowPolicyQuery struct {
916+
Position token.Position `json:"-"`
917+
IfExists bool `json:"if_exists,omitempty"`
918+
}
919+
920+
func (d *DropRowPolicyQuery) Pos() token.Position { return d.Position }
921+
func (d *DropRowPolicyQuery) End() token.Position { return d.Position }
922+
func (d *DropRowPolicyQuery) statementNode() {}
923+
924+
// ShowCreateRowPolicyQuery represents a SHOW CREATE ROW POLICY statement.
925+
type ShowCreateRowPolicyQuery struct {
926+
Position token.Position `json:"-"`
927+
}
928+
929+
func (s *ShowCreateRowPolicyQuery) Pos() token.Position { return s.Position }
930+
func (s *ShowCreateRowPolicyQuery) End() token.Position { return s.Position }
931+
func (s *ShowCreateRowPolicyQuery) statementNode() {}
932+
904933
// CreateIndexQuery represents a CREATE INDEX statement.
905934
type CreateIndexQuery struct {
906935
Position token.Position `json:"-"`

internal/explain/explain.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
143143
} else {
144144
fmt.Fprintf(sb, "%sSHOW CREATE SETTINGS PROFILE query\n", indent)
145145
}
146+
case *ast.CreateRowPolicyQuery:
147+
fmt.Fprintf(sb, "%sCREATE ROW POLICY or ALTER ROW POLICY query\n", indent)
148+
case *ast.DropRowPolicyQuery:
149+
fmt.Fprintf(sb, "%sDROP ROW POLICY query\n", indent)
150+
case *ast.ShowCreateRowPolicyQuery:
151+
fmt.Fprintf(sb, "%sSHOW CREATE ROW POLICY query\n", indent)
146152
case *ast.ShowGrantsQuery:
147153
fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent)
148154
case *ast.GrantQuery:

parser/parser.go

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ func (p *Parser) parseStatement() ast.Statement {
131131
if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" {
132132
return p.parseDropSettingsProfile()
133133
}
134+
// Check for DROP ROW POLICY or DROP POLICY
135+
if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") {
136+
return p.parseDropRowPolicy()
137+
}
134138
return p.parseDrop()
135139
case token.ALTER:
136140
// Check for ALTER USER
@@ -145,6 +149,10 @@ func (p *Parser) parseStatement() ast.Statement {
145149
if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" {
146150
return p.parseAlterSettingsProfile()
147151
}
152+
// Check for ALTER ROW POLICY or ALTER POLICY
153+
if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") {
154+
return p.parseAlterRowPolicy()
155+
}
148156
return p.parseAlter()
149157
case token.TRUNCATE:
150158
return p.parseTruncate()
@@ -1352,7 +1360,13 @@ func (p *Parser) parseCreate() ast.Statement {
13521360
case "PROFILE":
13531361
// CREATE PROFILE (without SETTINGS keyword)
13541362
return p.parseCreateSettingsProfile(pos)
1355-
case "RESOURCE", "WORKLOAD", "POLICY", "ROLE", "QUOTA":
1363+
case "ROW":
1364+
// CREATE ROW POLICY
1365+
return p.parseCreateRowPolicy(pos)
1366+
case "POLICY":
1367+
// CREATE POLICY (without ROW keyword)
1368+
return p.parseCreateRowPolicy(pos)
1369+
case "RESOURCE", "WORKLOAD", "ROLE", "QUOTA":
13561370
// Skip these statements - just consume tokens until semicolon
13571371
p.parseCreateGeneric(create)
13581372
default:
@@ -2064,6 +2078,112 @@ func (p *Parser) parseShowCreateSettingsProfile(pos token.Position) *ast.ShowCre
20642078
return query
20652079
}
20662080

2081+
func (p *Parser) parseCreateRowPolicy(pos token.Position) *ast.CreateRowPolicyQuery {
2082+
query := &ast.CreateRowPolicyQuery{
2083+
Position: pos,
2084+
}
2085+
2086+
// Skip ROW if present (CREATE ROW POLICY vs CREATE POLICY)
2087+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" {
2088+
p.nextToken()
2089+
}
2090+
2091+
// Skip POLICY
2092+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" {
2093+
p.nextToken()
2094+
}
2095+
2096+
// Skip the rest of the statement (policy names, ON table, etc.)
2097+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2098+
p.nextToken()
2099+
}
2100+
2101+
return query
2102+
}
2103+
2104+
func (p *Parser) parseDropRowPolicy() *ast.DropRowPolicyQuery {
2105+
query := &ast.DropRowPolicyQuery{
2106+
Position: p.current.Pos,
2107+
}
2108+
2109+
p.nextToken() // skip DROP
2110+
2111+
// Skip ROW if present (DROP ROW POLICY vs DROP POLICY)
2112+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" {
2113+
p.nextToken()
2114+
}
2115+
2116+
// Skip POLICY
2117+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" {
2118+
p.nextToken()
2119+
}
2120+
2121+
// Handle IF EXISTS
2122+
if p.currentIs(token.IF) {
2123+
p.nextToken()
2124+
if p.currentIs(token.EXISTS) {
2125+
query.IfExists = true
2126+
p.nextToken()
2127+
}
2128+
}
2129+
2130+
// Skip the rest of the statement (policy names, ON table, etc.)
2131+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2132+
p.nextToken()
2133+
}
2134+
2135+
return query
2136+
}
2137+
2138+
func (p *Parser) parseAlterRowPolicy() *ast.CreateRowPolicyQuery {
2139+
query := &ast.CreateRowPolicyQuery{
2140+
Position: p.current.Pos,
2141+
IsAlter: true,
2142+
}
2143+
2144+
p.nextToken() // skip ALTER
2145+
2146+
// Skip ROW if present (ALTER ROW POLICY vs ALTER POLICY)
2147+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" {
2148+
p.nextToken()
2149+
}
2150+
2151+
// Skip POLICY
2152+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" {
2153+
p.nextToken()
2154+
}
2155+
2156+
// Skip the rest of the statement (policy names, ON table, etc.)
2157+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2158+
p.nextToken()
2159+
}
2160+
2161+
return query
2162+
}
2163+
2164+
func (p *Parser) parseShowCreateRowPolicy(pos token.Position) *ast.ShowCreateRowPolicyQuery {
2165+
query := &ast.ShowCreateRowPolicyQuery{
2166+
Position: pos,
2167+
}
2168+
2169+
// Skip ROW if present (SHOW CREATE ROW POLICY vs SHOW CREATE POLICY)
2170+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" {
2171+
p.nextToken()
2172+
}
2173+
2174+
// Skip POLICY
2175+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" {
2176+
p.nextToken()
2177+
}
2178+
2179+
// Skip the rest of the statement (policy names, ON table, etc.)
2180+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2181+
p.nextToken()
2182+
}
2183+
2184+
return query
2185+
}
2186+
20672187
func (p *Parser) parseCreateDictionary(create *ast.CreateQuery) {
20682188
// Handle IF NOT EXISTS
20692189
if p.currentIs(token.IF) {
@@ -3717,6 +3837,9 @@ func (p *Parser) parseShow() ast.Statement {
37173837
} else if p.currentIs(token.SETTINGS) || (p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE") {
37183838
// SHOW CREATE SETTINGS PROFILE or SHOW CREATE PROFILE
37193839
return p.parseShowCreateSettingsProfile(pos)
3840+
} else if p.currentIs(token.IDENT) && (strings.ToUpper(p.current.Value) == "ROW" || strings.ToUpper(p.current.Value) == "POLICY") {
3841+
// SHOW CREATE ROW POLICY or SHOW CREATE POLICY
3842+
return p.parseShowCreateRowPolicy(pos)
37203843
} else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" {
37213844
show.ShowType = ast.ShowCreateDictionary
37223845
p.nextToken()
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt11": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,7 @@
11
{
22
"explain_todo": {
3-
"stmt1": true,
4-
"stmt11": true,
5-
"stmt12": true,
6-
"stmt13": true,
7-
"stmt14": true,
8-
"stmt16": true,
9-
"stmt17": true,
10-
"stmt18": true,
11-
"stmt19": true,
12-
"stmt2": true,
13-
"stmt21": true,
14-
"stmt22": true,
15-
"stmt23": true,
16-
"stmt24": true,
17-
"stmt25": true,
18-
"stmt26": true,
19-
"stmt27": true,
20-
"stmt28": true,
21-
"stmt29": true,
22-
"stmt3": true,
233
"stmt31": true,
24-
"stmt33": true,
25-
"stmt34": true,
26-
"stmt35": true,
27-
"stmt36": true,
28-
"stmt37": true,
29-
"stmt38": true,
30-
"stmt39": true,
31-
"stmt4": true,
32-
"stmt40": true,
33-
"stmt41": true,
34-
"stmt42": true,
35-
"stmt43": true,
36-
"stmt44": true,
37-
"stmt45": true,
38-
"stmt46": true,
39-
"stmt47": true,
40-
"stmt48": true,
41-
"stmt49": true,
42-
"stmt50": true,
43-
"stmt51": true,
44-
"stmt53": true,
45-
"stmt54": true,
46-
"stmt55": true,
47-
"stmt56": true,
48-
"stmt57": true,
49-
"stmt58": true,
50-
"stmt59": true,
514
"stmt6": true,
52-
"stmt60": true,
53-
"stmt61": true,
54-
"stmt62": true,
55-
"stmt63": true,
56-
"stmt65": true,
57-
"stmt66": true,
58-
"stmt67": true,
59-
"stmt69": true,
60-
"stmt70": true,
61-
"stmt8": true,
62-
"stmt9": true
5+
"stmt70": true
636
}
647
}
Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt1": true,
4-
"stmt10": true,
5-
"stmt11": true,
6-
"stmt12": true,
7-
"stmt13": true,
8-
"stmt14": true,
9-
"stmt15": true,
10-
"stmt17": true,
11-
"stmt19": true,
12-
"stmt2": true,
13-
"stmt20": true,
14-
"stmt21": true,
15-
"stmt22": true,
16-
"stmt23": true,
17-
"stmt24": true,
18-
"stmt25": true,
19-
"stmt26": true,
20-
"stmt27": true,
21-
"stmt28": true,
22-
"stmt29": true,
23-
"stmt30": true,
24-
"stmt31": true,
25-
"stmt32": true,
26-
"stmt33": true,
27-
"stmt34": true,
28-
"stmt35": true,
29-
"stmt36": true,
30-
"stmt37": true,
31-
"stmt38": true,
32-
"stmt39": true,
33-
"stmt40": true,
34-
"stmt41": true,
35-
"stmt42": true,
36-
"stmt43": true,
37-
"stmt44": true,
38-
"stmt8": true,
39-
"stmt9": true
40-
}
41-
}
1+
{}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt6": true,
4-
"stmt7": true,
5-
"stmt9": true
6-
}
7-
}
1+
{}

parser/testdata/01702_system_query_log/metadata.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
"stmt14": true,
44
"stmt15": true,
55
"stmt16": true,
6-
"stmt17": true,
7-
"stmt18": true,
86
"stmt19": true,
97
"stmt24": true,
108
"stmt26": true,
@@ -38,16 +36,12 @@
3836
"stmt57": true,
3937
"stmt58": true,
4038
"stmt59": true,
41-
"stmt6": true,
4239
"stmt60": true,
4340
"stmt68": true,
4441
"stmt69": true,
45-
"stmt7": true,
4642
"stmt74": true,
4743
"stmt8": true,
4844
"stmt83": true,
49-
"stmt84": true,
50-
"stmt85": true,
5145
"stmt86": true
5246
}
5347
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt4": true,
4-
"stmt6": true
5-
}
6-
}
1+
{}

parser/testdata/01710_projections/metadata.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
{
22
"explain_todo": {
3-
"stmt10": true,
4-
"stmt11": true,
5-
"stmt13": true,
63
"stmt2": true,
74
"stmt22": true
85
}

0 commit comments

Comments
 (0)