getQueryType classifies a single parsed Snowflake statement into a base.QueryType. It mirrors the legacy ANTLR listener's getQueryTypeForBatch/getQueryTypeForDmlCommand/getQueryTypeForOtherCommand (which switched on the sql_command / dml_command / other_command grammar branches) onto a type-switch o
(node ast.Node)
| 42 | // |
| 43 | // A nil node yields base.QueryTypeUnknown. |
| 44 | func getQueryType(node ast.Node) base.QueryType { |
| 45 | if node == nil { |
| 46 | return base.QueryTypeUnknown |
| 47 | } |
| 48 | switch n := node.(type) { |
| 49 | // SELECT / set operations. |
| 50 | case *ast.SelectStmt, *ast.SetOperationStmt: |
| 51 | return base.Select |
| 52 | |
| 53 | // DML: INSERT (single + multi-table), UPDATE, DELETE, MERGE. The legacy |
| 54 | // listener also classified CALL / EXECUTE IMMEDIATE / EXECUTE TASK as DML |
| 55 | // (other_command branches) — a stored procedure can mutate data, so DML is |
| 56 | // the right ACL bucket, not the DDL fallback. |
| 57 | case *ast.InsertStmt, *ast.InsertMultiStmt, |
| 58 | *ast.UpdateStmt, *ast.DeleteStmt, *ast.MergeStmt, |
| 59 | *ast.CallStmt, *ast.ExecuteImmediateStmt, *ast.ExecuteTaskStmt: |
| 60 | return base.DML |
| 61 | |
| 62 | // SHOW / DESCRIBE read system metadata. |
| 63 | case *ast.ResultScanStmt: |
| 64 | // stmt ->> query: the result shape is the trailing query's (typically a |
| 65 | // SELECT over $1). Read-only-ness of the SOURCE is enforced separately |
| 66 | // in classifyForEditor. |
| 67 | return getQueryType(n.Query) |
| 68 | case *ast.ExplainStmt: |
| 69 | // EXPLAIN is read-only and data-returning regardless of the inner |
| 70 | // statement (legacy other_command.explain never recursed into it). |
| 71 | return base.Explain |
| 72 | case *ast.ShowStmt: |
| 73 | // A SHOW with a result-pipe (SHOW ... ->> <query>) produces the piped |
| 74 | // query's result — classify by it, so the trailing SELECT is |
| 75 | // permission-checked/masked as a query instead of hiding behind |
| 76 | // info-schema-only access. |
| 77 | if n.Pipe != nil { |
| 78 | return getQueryType(n.Pipe) |
| 79 | } |
| 80 | return base.SelectInfoSchema |
| 81 | case *ast.DescribeStmt: |
| 82 | return base.SelectInfoSchema |
| 83 | |
| 84 | // USE is a session-scoped no-op for read purposes; the legacy listener |
| 85 | // classified use_command as base.Select. |
| 86 | case *ast.UseStmt: |
| 87 | return base.Select |
| 88 | |
| 89 | // SET (session variable) was classified base.Select by the legacy listener. |
| 90 | case *ast.SetStmt: |
| 91 | return base.Select |
| 92 | |
| 93 | // COPY INTO <table> loads data, so the legacy listener classified it DML. |
| 94 | case *ast.CopyIntoTableStmt: |
| 95 | return base.DML |
| 96 | |
| 97 | // COMMENT ON ... is DDL. |
| 98 | case *ast.CommentStmt: |
| 99 | return base.DDL |
| 100 | } |
| 101 |