@@ -135,6 +135,10 @@ func (p *Parser) parseStatement() ast.Statement {
135135 if p .peek .Token == token .IDENT && (strings .ToUpper (p .peek .Value ) == "ROW" || strings .ToUpper (p .peek .Value ) == "POLICY" ) {
136136 return p .parseDropRowPolicy ()
137137 }
138+ // Check for DROP ROLE
139+ if p .peek .Token == token .IDENT && strings .ToUpper (p .peek .Value ) == "ROLE" {
140+ return p .parseDropRole ()
141+ }
138142 return p .parseDrop ()
139143 case token .ALTER :
140144 // Check for ALTER USER
@@ -153,6 +157,10 @@ func (p *Parser) parseStatement() ast.Statement {
153157 if p .peek .Token == token .IDENT && (strings .ToUpper (p .peek .Value ) == "ROW" || strings .ToUpper (p .peek .Value ) == "POLICY" ) {
154158 return p .parseAlterRowPolicy ()
155159 }
160+ // Check for ALTER ROLE
161+ if p .peek .Token == token .IDENT && strings .ToUpper (p .peek .Value ) == "ROLE" {
162+ return p .parseAlterRole ()
163+ }
156164 return p .parseAlter ()
157165 case token .TRUNCATE :
158166 return p .parseTruncate ()
@@ -1366,7 +1374,10 @@ func (p *Parser) parseCreate() ast.Statement {
13661374 case "POLICY" :
13671375 // CREATE POLICY (without ROW keyword)
13681376 return p .parseCreateRowPolicy (pos )
1369- case "RESOURCE" , "WORKLOAD" , "ROLE" , "QUOTA" :
1377+ case "ROLE" :
1378+ // CREATE ROLE
1379+ return p .parseCreateRole (pos )
1380+ case "RESOURCE" , "WORKLOAD" , "QUOTA" :
13701381 // Skip these statements - just consume tokens until semicolon
13711382 p .parseCreateGeneric (create )
13721383 default :
@@ -2184,6 +2195,119 @@ func (p *Parser) parseShowCreateRowPolicy(pos token.Position) *ast.ShowCreateRow
21842195 return query
21852196}
21862197
2198+ func (p * Parser ) parseCreateRole (pos token.Position ) * ast.CreateRoleQuery {
2199+ query := & ast.CreateRoleQuery {
2200+ Position : pos ,
2201+ }
2202+
2203+ // Skip ROLE
2204+ if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "ROLE" {
2205+ p .nextToken ()
2206+ }
2207+
2208+ // Skip the rest of the statement (role names, SETTINGS, etc.)
2209+ for ! p .currentIs (token .EOF ) && ! p .currentIs (token .SEMICOLON ) {
2210+ p .nextToken ()
2211+ }
2212+
2213+ return query
2214+ }
2215+
2216+ func (p * Parser ) parseDropRole () * ast.DropRoleQuery {
2217+ query := & ast.DropRoleQuery {
2218+ Position : p .current .Pos ,
2219+ }
2220+
2221+ p .nextToken () // skip DROP
2222+
2223+ // Skip ROLE
2224+ if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "ROLE" {
2225+ p .nextToken ()
2226+ }
2227+
2228+ // Handle IF EXISTS
2229+ if p .currentIs (token .IF ) {
2230+ p .nextToken ()
2231+ if p .currentIs (token .EXISTS ) {
2232+ query .IfExists = true
2233+ p .nextToken ()
2234+ }
2235+ }
2236+
2237+ // Skip the rest of the statement (role names, etc.)
2238+ for ! p .currentIs (token .EOF ) && ! p .currentIs (token .SEMICOLON ) {
2239+ p .nextToken ()
2240+ }
2241+
2242+ return query
2243+ }
2244+
2245+ func (p * Parser ) parseAlterRole () * ast.CreateRoleQuery {
2246+ query := & ast.CreateRoleQuery {
2247+ Position : p .current .Pos ,
2248+ IsAlter : true ,
2249+ }
2250+
2251+ p .nextToken () // skip ALTER
2252+
2253+ // Skip ROLE
2254+ if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "ROLE" {
2255+ p .nextToken ()
2256+ }
2257+
2258+ // Skip the rest of the statement (role names, SETTINGS, RENAME TO, etc.)
2259+ for ! p .currentIs (token .EOF ) && ! p .currentIs (token .SEMICOLON ) {
2260+ p .nextToken ()
2261+ }
2262+
2263+ return query
2264+ }
2265+
2266+ func (p * Parser ) parseShowCreateRole (pos token.Position ) * ast.ShowCreateRoleQuery {
2267+ query := & ast.ShowCreateRoleQuery {
2268+ Position : pos ,
2269+ RoleCount : 1 , // Default to 1 role
2270+ }
2271+
2272+ // Skip ROLE
2273+ if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "ROLE" {
2274+ p .nextToken ()
2275+ }
2276+
2277+ // Count role names (separated by commas)
2278+ // Skip first role name
2279+ for p .currentIs (token .IDENT ) || p .currentIs (token .STRING ) || p .current .Token .IsKeyword () {
2280+ p .nextToken ()
2281+ // Handle role@host syntax
2282+ if p .currentIs (token .IDENT ) && strings .HasPrefix (p .current .Value , "@" ) {
2283+ p .nextToken ()
2284+ }
2285+ break
2286+ }
2287+
2288+ // Count additional roles
2289+ for p .currentIs (token .COMMA ) {
2290+ query .RoleCount ++
2291+ p .nextToken ()
2292+ // Skip role name
2293+ for p .currentIs (token .IDENT ) || p .currentIs (token .STRING ) || p .current .Token .IsKeyword () {
2294+ p .nextToken ()
2295+ // Handle role@host syntax
2296+ if p .currentIs (token .IDENT ) && strings .HasPrefix (p .current .Value , "@" ) {
2297+ p .nextToken ()
2298+ }
2299+ break
2300+ }
2301+ }
2302+
2303+ // Skip the rest of the statement
2304+ for ! p .currentIs (token .EOF ) && ! p .currentIs (token .SEMICOLON ) {
2305+ p .nextToken ()
2306+ }
2307+
2308+ return query
2309+ }
2310+
21872311func (p * Parser ) parseCreateDictionary (create * ast.CreateQuery ) {
21882312 // Handle IF NOT EXISTS
21892313 if p .currentIs (token .IF ) {
@@ -3840,6 +3964,9 @@ func (p *Parser) parseShow() ast.Statement {
38403964 } else if p .currentIs (token .IDENT ) && (strings .ToUpper (p .current .Value ) == "ROW" || strings .ToUpper (p .current .Value ) == "POLICY" ) {
38413965 // SHOW CREATE ROW POLICY or SHOW CREATE POLICY
38423966 return p .parseShowCreateRowPolicy (pos )
3967+ } else if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "ROLE" {
3968+ // SHOW CREATE ROLE
3969+ return p .parseShowCreateRole (pos )
38433970 } else if p .currentIs (token .IDENT ) && strings .ToUpper (p .current .Value ) == "DICTIONARY" {
38443971 show .ShowType = ast .ShowCreateDictionary
38453972 p .nextToken ()
0 commit comments