Skip to content

Commit fa4f6e6

Browse files
localstack-spiral[bot]spiral
andauthored
LAV-3054: Cover accepted REVOKE schema privilege vocabulary (#3412)
Capture Enterprise Snowflake grant, revoke, and SHOW GRANTS results for all 68 documented schema privileges. Extend the SQL parser for their CREATE alternatives and map SNAPSHOT POLICY/SET grant rows to Cloud's BACKUP POLICY/SET spelling. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Test matrix ADD SEARCH OPTIMIZATION × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[add_search_optimization] APPLYBUDGET × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[applybudget] CREATE AGENT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_agent] CREATE ALERT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_alert] CREATE APPLICATION SERVICE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_application_service] CREATE ARTIFACT REPOSITORY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_artifact_repository] CREATE CONTACT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_contact] CREATE CORTEX SEARCH SERVICE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_cortex_search_service] CREATE DATA METRIC FUNCTION × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_data_metric_function] CREATE DATASET × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_dataset] CREATE DBT PROJECT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_dbt_project] CREATE EVENT TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_event_table] CREATE EXPERIMENT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_experiment] CREATE FILE FORMAT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_file_format] CREATE FUNCTION × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_function] CREATE GATEWAY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_gateway] CREATE GIT REPOSITORY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_git_repository] CREATE IMAGE REPOSITORY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_image_repository] CREATE MCP SERVER × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_mcp_server] CREATE MODEL × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_model] CREATE NETWORK RULE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_network_rule] CREATE NOTEBOOK × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_notebook] CREATE PIPE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_pipe] CREATE PROCEDURE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_procedure] CREATE AGGREGATION POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_aggregation_policy] CREATE AUTHENTICATION POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_authentication_policy] CREATE MASKING POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_masking_policy] CREATE PACKAGES POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_packages_policy] CREATE PASSWORD POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_password_policy] CREATE PRIVACY POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_privacy_policy] CREATE PROJECTION POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_projection_policy] CREATE ROW ACCESS POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_row_access_policy] CREATE SESSION POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_session_policy] CREATE STORAGE LIFECYCLE POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_storage_lifecycle_policy] CREATE SECRET × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_secret] CREATE SEQUENCE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_sequence] CREATE SERVICE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_service] CREATE SNAPSHOT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snapshot] CREATE SNAPSHOT POLICY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snapshot_policy] CREATE SNAPSHOT SET × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snapshot_set] CREATE STAGE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_stage] CREATE STREAM × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_stream] CREATE STREAMLIT × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_streamlit] CREATE SNOWFLAKE.CORE.BUDGET × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.core.budget] CREATE SNOWFLAKE.DATA_PRIVACY.CLASSIFICATION_PROFILE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.data_privacy.classification_profile] CREATE SNOWFLAKE.DATA_PRIVACY.CUSTOM_CLASSIFIER × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.data_privacy.custom_classifier] CREATE SNOWFLAKE.ML.ANOMALY_DETECTION × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.ml.anomaly_detection] CREATE SNOWFLAKE.ML.CLASSIFICATION × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.ml.classification] CREATE SNOWFLAKE.ML.FORECAST × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.ml.forecast] CREATE SNOWFLAKE.ML.TOP_INSIGHTS × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.ml.top_insights] CREATE SNOWFLAKE.ML.DOCUMENT_INTELLIGENCE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_snowflake.ml.document_intelligence] CREATE TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_table] CREATE DYNAMIC TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_dynamic_table] CREATE EXTERNAL TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_external_table] CREATE HYBRID TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_hybrid_table] CREATE ICEBERG TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_iceberg_table] CREATE INTERACTIVE TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_interactive_table] CREATE ONLINE FEATURE TABLE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_online_feature_table] CREATE TAG × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_tag] CREATE TASK × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_task] CREATE TYPE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_type] CREATE WORKSPACE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_workspace] CREATE VIEW × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_view] CREATE MATERIALIZED VIEW × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_materialized_view] CREATE SEMANTIC VIEW × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[create_semantic_view] MODIFY × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[modify] MONITOR × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[monitor] USAGE × grant/revoke/status/metadata -> tests/queries/access_control/test_revoke_schema_privilege_vocabulary.py::test_revoke_schema_privilege_vocabulary[usage] Invalid schema privilege × error -> uncovered: remaining value-space audit follow-up. ALL PRIVILEGES × expansion -> uncovered: separate scope. Bulk/future schema and database-role targets -> uncovered: separate scope. ## Deviations - The hint points to the existing grant vocabulary test for round trips. A separate parameterized REVOKE file keeps this complete accepted-value matrix and its status snapshots together without changing existing compat tests. - The reference names CREATE SNAPSHOT POLICY and CREATE SNAPSHOT SET; Cloud reports their grant metadata as CREATE BACKUP POLICY and CREATE BACKUP SET, which the grant store now records. Co-authored-by: spiral <spiral@localhost>
1 parent 33c2c7d commit fa4f6e6

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

‎src/parser/mod.rs‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20743,6 +20743,58 @@ impl<'a> Parser<'a> {
2074320743
}
2074420744

2074520745
fn maybe_parse_action_create_object_type(&mut self) -> Option<ActionCreateObjectType> {
20746+
// Schema privileges include object types that are not ordinary CREATE
20747+
// statement keywords. Match their complete token sequence before the
20748+
// existing single-word alternatives can consume a prefix.
20749+
const SCHEMA_TYPES: &[&str] = &[
20750+
"AGENT", "ALERT", "APPLICATION SERVICE", "ARTIFACT REPOSITORY",
20751+
"CONTACT", "CORTEX SEARCH SERVICE", "DATA METRIC FUNCTION",
20752+
"DATASET", "DBT PROJECT", "EVENT TABLE", "EXPERIMENT",
20753+
"FILE FORMAT", "FUNCTION", "GATEWAY", "GIT REPOSITORY",
20754+
"IMAGE REPOSITORY", "MCP SERVER", "MODEL", "NETWORK RULE",
20755+
"NOTEBOOK", "PIPE", "PROCEDURE", "AGGREGATION POLICY",
20756+
"AUTHENTICATION POLICY", "MASKING POLICY", "PACKAGES POLICY",
20757+
"PASSWORD POLICY", "PRIVACY POLICY", "PROJECTION POLICY",
20758+
"ROW ACCESS POLICY", "SESSION POLICY", "STORAGE LIFECYCLE POLICY",
20759+
"SECRET", "SEQUENCE", "SERVICE", "SNAPSHOT", "SNAPSHOT POLICY",
20760+
"SNAPSHOT SET", "STAGE", "STREAM", "STREAMLIT",
20761+
"SNOWFLAKE.CORE.BUDGET", "SNOWFLAKE.DATA_PRIVACY.CLASSIFICATION_PROFILE",
20762+
"SNOWFLAKE.DATA_PRIVACY.CUSTOM_CLASSIFIER",
20763+
"SNOWFLAKE.ML.ANOMALY_DETECTION", "SNOWFLAKE.ML.CLASSIFICATION",
20764+
"SNOWFLAKE.ML.FORECAST", "SNOWFLAKE.ML.TOP_INSIGHTS",
20765+
"SNOWFLAKE.ML.DOCUMENT_INTELLIGENCE", "TABLE", "DYNAMIC TABLE",
20766+
"EXTERNAL TABLE", "HYBRID TABLE", "ICEBERG TABLE",
20767+
"INTERACTIVE TABLE", "ONLINE FEATURE TABLE", "TAG", "TASK",
20768+
"TYPE", "WORKSPACE", "VIEW", "MATERIALIZED VIEW", "SEMANTIC VIEW",
20769+
];
20770+
let mut phrase = String::new();
20771+
let mut count = 0;
20772+
while count < 8 {
20773+
match &self.peek_nth_token_ref(count).token {
20774+
Token::Word(word) if word.keyword == Keyword::ON => break,
20775+
Token::Word(word) => {
20776+
if !phrase.is_empty() && !phrase.ends_with('.') {
20777+
phrase.push(' ');
20778+
}
20779+
phrase.push_str(&word.value.to_ascii_uppercase());
20780+
}
20781+
Token::Period => phrase.push('.'),
20782+
_ => break,
20783+
}
20784+
count += 1;
20785+
}
20786+
if count > 0
20787+
&& matches!(&self.peek_nth_token_ref(count).token, Token::Word(word) if word.keyword == Keyword::ON)
20788+
&& matches!(&self.peek_nth_token_ref(count + 1).token, Token::Word(word) if word.keyword == Keyword::SCHEMA)
20789+
&& SCHEMA_TYPES.contains(&phrase.as_str())
20790+
{
20791+
for _ in 0..count {
20792+
self.next_token();
20793+
}
20794+
return Some(ActionCreateObjectType::Class(ObjectName(vec![
20795+
ObjectNamePart::Identifier(Ident::new(phrase)),
20796+
])));
20797+
}
2074620798
// Multi-word object types
2074720799
if self.parse_keywords(&[Keyword::APPLICATION, Keyword::PACKAGE]) {
2074820800
Some(ActionCreateObjectType::ApplicationPackage)

0 commit comments

Comments
 (0)