Skip to content

Commit f8135e7

Browse files
localstack-spiral[bot]spiralsabir-akhadov-localstack
authored
LAV-3525: Persist row-access policy comments across CREATE and ALTER (#3866)
Parse and persist CREATE comments, including CREATE OR ALTER's clearing of omitted comments, without changing the SHOW or DESC projections. Support ALTER SET/UNSET COMMENT with IF EXISTS and Cloud's alternate literal forms. Validate comment clauses at their AST expression boundary before mutation, and match the recorded wire errors, including their query-context shape. Verification: - Enterprise Cloud captures: 102 new parameterized cases, captured serially. - make check: passed. - New comment compat file: 102 passed. - Existing body-update, view-policy, drop-table and RBAC row-access tests: 122 passed, 238 deselected; no existing test/snapshot edits. - make test-vendored-sqlparser: passed. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Deviations Cloud returns an internal-error incident for CREATE COMMENT = NULL, while ALTER SET COMMENT = NULL clears the comment. Implemented these recorded behaviors and normalized incident IDs in the new snapshots using the existing recipe in tests/queries/access_control/test_grant_plural_rejected.py:27. The CREATE atomicity snapshots exposed an existing duplicate-object error that discarded explicit name qualifiers. Pass the requested qualified name to the lifecycle UDF for that error; qualified and unqualified cases are both captured, without changing the stored policy name or metadata columns. ## Test matrix Resource-index scope: CREATE/CREATE OR ALTER COMMENT and ALTER SET/UNSET COMMENT; RENAME, SET BODY and TAG operations are unchanged. Existing RENAME and SET BODY cases were included in the row-access regression selection. alter_row_access_policy_comment_existence[SET COMMENT = 'after'--False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 'after'--False] alter_row_access_policy_comment_existence[SET COMMENT = 'after'--True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 'after'--True] alter_row_access_policy_comment_existence[SET COMMENT = 'after'-IF EXISTS-False] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 'after'-IF EXISTS-False] alter_row_access_policy_comment_existence[SET COMMENT = 'after'-IF EXISTS-True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 'after'-IF EXISTS-True] alter_row_access_policy_comment_existence[SET COMMENT = 123--False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 123--False] alter_row_access_policy_comment_existence[SET COMMENT = 123--True] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 123--True] alter_row_access_policy_comment_existence[SET COMMENT = 123-IF EXISTS-False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 123-IF EXISTS-False] alter_row_access_policy_comment_existence[SET COMMENT = 123-IF EXISTS-True] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = 123-IF EXISTS-True] alter_row_access_policy_comment_existence[SET COMMENT = E'bad'--False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = E'bad'--False] alter_row_access_policy_comment_existence[SET COMMENT = E'bad'--True] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = E'bad'--True] alter_row_access_policy_comment_existence[SET COMMENT = E'bad'-IF EXISTS-False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = E'bad'-IF EXISTS-False] alter_row_access_policy_comment_existence[SET COMMENT = E'bad'-IF EXISTS-True] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = E'bad'-IF EXISTS-True] alter_row_access_policy_comment_existence[SET COMMENT = NULL--False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = NULL--False] alter_row_access_policy_comment_existence[SET COMMENT = NULL--True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = NULL--True] alter_row_access_policy_comment_existence[SET COMMENT = NULL-IF EXISTS-False] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = NULL-IF EXISTS-False] alter_row_access_policy_comment_existence[SET COMMENT = NULL-IF EXISTS-True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[SET COMMENT = NULL-IF EXISTS-True] alter_row_access_policy_comment_existence[UNSET COMMENT--False] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[UNSET COMMENT--False] alter_row_access_policy_comment_existence[UNSET COMMENT--True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[UNSET COMMENT--True] alter_row_access_policy_comment_existence[UNSET COMMENT-IF EXISTS-False] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[UNSET COMMENT-IF EXISTS-False] alter_row_access_policy_comment_existence[UNSET COMMENT-IF EXISTS-True] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_alter_row_access_policy_comment_existence[UNSET COMMENT-IF EXISTS-True] create_row_access_policy_omitted_comment × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_create_row_access_policy_omitted_comment row_access_policy_comment_clause_scope["COMMENT"-"COMMENT" = 1] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope["COMMENT"-"COMMENT" = 1] row_access_policy_comment_clause_scope[comment-comment = 1] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope[comment-comment = 1] row_access_policy_comment_clause_scope[id-$$COMMENT = E'bad'$$ = $$COMMENT = E'bad'$$] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope[id-$$COMMENT = E'bad'$$ = $$COMMENT = E'bad'$$] row_access_policy_comment_clause_scope[id-'COMMENT = NULL' = 'COMMENT = NULL'] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope[id-'COMMENT = NULL' = 'COMMENT = NULL'] row_access_policy_comment_clause_scope[id-id -- COMMENT = NULL\n = 1] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope[id-id -- COMMENT = NULL\n = 1] row_access_policy_comment_clause_scope[id-id /* COMMENT = 123 */ = 1] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_clause_scope[id-id /* COMMENT = 123 */ = 1] row_access_policy_comment_create_flags × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags row_access_policy_comment_lifecycle × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle row_access_policy_comment_literals[backslash-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[backslash-create] row_access_policy_comment_literals[backslash-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[backslash-create_or_alter] row_access_policy_comment_literals[backslash-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[backslash-set] row_access_policy_comment_literals[boolean-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[boolean-create] row_access_policy_comment_literals[boolean-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[boolean-create_or_alter] row_access_policy_comment_literals[boolean-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[boolean-set] row_access_policy_comment_literals[dollar-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[dollar-create] row_access_policy_comment_literals[dollar-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[dollar-create_or_alter] row_access_policy_comment_literals[dollar-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[dollar-set] row_access_policy_comment_literals[doubled_quote-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[doubled_quote-create] row_access_policy_comment_literals[doubled_quote-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[doubled_quote-create_or_alter] row_access_policy_comment_literals[doubled_quote-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[doubled_quote-set] row_access_policy_comment_literals[empty-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[empty-create] row_access_policy_comment_literals[empty-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[empty-create_or_alter] row_access_policy_comment_literals[empty-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[empty-set] row_access_policy_comment_literals[escaped_prefix-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[escaped_prefix-create] row_access_policy_comment_literals[escaped_prefix-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[escaped_prefix-create_or_alter] row_access_policy_comment_literals[escaped_prefix-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[escaped_prefix-set] row_access_policy_comment_literals[expression-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[expression-create] row_access_policy_comment_literals[expression-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[expression-create_or_alter] row_access_policy_comment_literals[expression-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[expression-set] row_access_policy_comment_literals[identifier-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[identifier-create] row_access_policy_comment_literals[identifier-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[identifier-create_or_alter] row_access_policy_comment_literals[identifier-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[identifier-set] row_access_policy_comment_literals[multiline-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[multiline-create] row_access_policy_comment_literals[multiline-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[multiline-create_or_alter] row_access_policy_comment_literals[multiline-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[multiline-set] row_access_policy_comment_literals[negative-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[negative-create] row_access_policy_comment_literals[negative-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[negative-create_or_alter] row_access_policy_comment_literals[negative-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[negative-set] row_access_policy_comment_literals[null-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[null-create] row_access_policy_comment_literals[null-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[null-create_or_alter] row_access_policy_comment_literals[null-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[null-set] row_access_policy_comment_literals[number-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[number-create] row_access_policy_comment_literals[number-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[number-create_or_alter] row_access_policy_comment_literals[number-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[number-set] row_access_policy_comment_literals[ordinary-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[ordinary-create] row_access_policy_comment_literals[ordinary-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[ordinary-create_or_alter] row_access_policy_comment_literals[ordinary-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[ordinary-set] row_access_policy_comment_literals[tagged_dollar-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_dollar-create] row_access_policy_comment_literals[tagged_dollar-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_dollar-create_or_alter] row_access_policy_comment_literals[tagged_dollar-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_dollar-set] row_access_policy_comment_literals[tagged_plain-create] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_plain-create] row_access_policy_comment_literals[tagged_plain-create_or_alter] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_plain-create_or_alter] row_access_policy_comment_literals[tagged_plain-set] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[tagged_plain-set] row_access_policy_comment_literals[unicode-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unicode-create] row_access_policy_comment_literals[unicode-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unicode-create_or_alter] row_access_policy_comment_literals[unicode-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unicode-set] row_access_policy_comment_literals[unquoted-create] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unquoted-create] row_access_policy_comment_literals[unquoted-create_or_alter] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unquoted-create_or_alter] row_access_policy_comment_literals[unquoted-set] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_literals[unquoted-set] row_access_policy_create_comment_syntax[COMMENT 'after'-CREATE OR ALTER] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT 'after'-CREATE OR ALTER] row_access_policy_create_comment_syntax[COMMENT 'after'-CREATE OR REPLACE] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT 'after'-CREATE OR REPLACE] row_access_policy_create_comment_syntax[COMMENT = 'after' extra-CREATE OR ALTER] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT = 'after' extra-CREATE OR ALTER] row_access_policy_create_comment_syntax[COMMENT = 'after' extra-CREATE OR REPLACE] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT = 'after' extra-CREATE OR REPLACE] row_access_policy_create_comment_syntax[COMMENT = E-CREATE OR ALTER] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT = E-CREATE OR ALTER] row_access_policy_create_comment_syntax[COMMENT = E-CREATE OR REPLACE] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT = E-CREATE OR REPLACE] row_access_policy_create_comment_syntax[COMMENT =-CREATE OR ALTER] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT =-CREATE OR ALTER] row_access_policy_create_comment_syntax[COMMENT =-CREATE OR REPLACE] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT =-CREATE OR REPLACE] row_access_policy_create_comment_syntax[COMMENT-CREATE OR ALTER] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT-CREATE OR ALTER] row_access_policy_create_comment_syntax[COMMENT-CREATE OR REPLACE] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_create_comment_syntax[COMMENT-CREATE OR REPLACE] row_access_policy_invalid_comment_preserves_definition[-SET COMMENT 'after'] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-SET COMMENT 'after'] row_access_policy_invalid_comment_preserves_definition[-SET COMMENT = 'after' extra] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-SET COMMENT = 'after' extra] row_access_policy_invalid_comment_preserves_definition[-SET COMMENT = NULL] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-SET COMMENT = NULL] row_access_policy_invalid_comment_preserves_definition[-SET COMMENT =] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-SET COMMENT =] row_access_policy_invalid_comment_preserves_definition[-SET COMMENT] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-SET COMMENT] row_access_policy_invalid_comment_preserves_definition[-UNSET COMMENT = 'after'] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[-UNSET COMMENT = 'after'] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT 'after'] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT 'after'] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT = 'after' extra] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT = 'after' extra] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT = NULL] × happy/boundary + read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT = NULL] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT =] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT =] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-SET COMMENT] row_access_policy_invalid_comment_preserves_definition[IF EXISTS-UNSET COMMENT = 'after'] × error + unchanged read-back -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_invalid_comment_preserves_definition[IF EXISTS-UNSET COMMENT = 'after'] CREATE OR ALTER new × populated comment -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle CREATE OR ALTER existing × changed comment/body -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle CREATE OR ALTER existing × omission clears comment -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle CREATE OR ALTER existing × explicit empty comment -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle CREATE OR ALTER × table/view attachments and changed body enforcement -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle ALTER SET escaped/empty comment × attached table/view body preservation -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle ALTER UNSET/repeated UNSET × attached table/view body preservation -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle Invalid CREATE OR ALTER numeric/ALTER prefixed literal × attached definition preservation -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_lifecycle CREATE IF NOT EXISTS × populated policy unchanged -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags CREATE duplicate qualified/unqualified name × unchanged definition and exact error -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags CREATE OR REPLACE × populated comment -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags CREATE OR REPLACE × omitted comment -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags CREATE OR REPLACE numeric comment × unchanged definition -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags CREATE OR ALTER signature rejection × unchanged comment/definition -> tests/queries/ddl/test_row_access_policy_comments.py::test_row_access_policy_comment_create_flags Unchanged RENAME × present/missing/IF EXISTS -> tests/queries/access_control/test_rbac.py::test_crud_for_row_access_policies Unchanged SET BODY × attached/error/boundary -> tests/queries/ddl/test_row_access_policy_body_updates.py Co-authored-by: spiral <spiral@localhost> Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
1 parent b419f33 commit f8135e7

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

‎src/ast/ddl.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,13 @@ pub enum AlterMaskingPolicyOperation {
16111611
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16121612
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
16131613
pub enum AlterRowAccessPolicyOperation {
1614+
/// `SET COMMENT = '<comment>'`.
1615+
SetComment {
1616+
/// The replacement comment.
1617+
comment: String,
1618+
},
1619+
/// `UNSET COMMENT`.
1620+
UnsetComment,
16141621
/// `SET BODY -> <expr>`.
16151622
SetBody {
16161623
/// The replacement body expression.
@@ -1626,6 +1633,10 @@ pub enum AlterRowAccessPolicyOperation {
16261633
impl fmt::Display for AlterRowAccessPolicyOperation {
16271634
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16281635
match self {
1636+
Self::SetComment { comment } => {
1637+
write!(f, "SET COMMENT = '{}'", escape_single_quote_string(comment))
1638+
}
1639+
Self::UnsetComment => write!(f, "UNSET COMMENT"),
16291640
Self::SetBody { body } => write!(f, "SET BODY -> {body}"),
16301641
Self::RenameTo { new_name } => write!(f, "RENAME TO {new_name}"),
16311642
}

‎src/ast/mod.rs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5776,6 +5776,8 @@ pub enum Statement {
57765776
return_type: DataType,
57775777
/// The policy body expression after `->`.
57785778
policy_expr: Expr,
5779+
/// Optional policy comment.
5780+
comment: Option<String>,
57795781
},
57805782
/// ```sql
57815783
/// ALTER ROW ACCESS POLICY [IF EXISTS] <name> RENAME TO <new_name>
@@ -9504,14 +9506,23 @@ impl fmt::Display for Statement {
95049506
args,
95059507
return_type,
95069508
policy_expr,
9509+
comment,
95079510
} => {
95089511
write!(
95099512
f,
95109513
"CREATE {or_replace}ROW ACCESS POLICY {if_not_exists}{name} AS ({args}) RETURNS {return_type} -> {policy_expr}",
95119514
or_replace = if *or_replace { "OR REPLACE " } else if *or_alter { "OR ALTER " } else { "" },
95129515
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
95139516
args = display_comma_separated(args),
9514-
)
9517+
)?;
9518+
if let Some(comment) = comment {
9519+
write!(
9520+
f,
9521+
" COMMENT = '{}'",
9522+
value::escape_single_quote_string(comment)
9523+
)?;
9524+
}
9525+
Ok(())
95159526
}
95169527
Statement::AlterRowAccessPolicy {
95179528
if_exists,

‎src/dialect/snowflake.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5136,6 +5136,12 @@ fn parse_create_row_access_policy(
51365136
let return_type = parser.parse_data_type()?;
51375137
parser.expect_token(&Token::Arrow)?;
51385138
let policy_expr = parser.parse_expr()?;
5139+
let comment = if parser.parse_keyword(Keyword::COMMENT) {
5140+
parser.expect_token(&Token::Eq)?;
5141+
Some(parse_row_access_policy_comment(parser)?)
5142+
} else {
5143+
None
5144+
};
51395145
Ok(Statement::CreateRowAccessPolicy {
51405146
or_replace,
51415147
or_alter,
@@ -5144,6 +5150,7 @@ fn parse_create_row_access_policy(
51445150
args,
51455151
return_type,
51465152
policy_expr,
5153+
comment,
51475154
})
51485155
}
51495156

@@ -5156,6 +5163,17 @@ fn parse_alter_row_access_policy(parser: &mut Parser) -> Result<Statement, Parse
51565163
crate::ast::AlterRowAccessPolicyOperation::SetBody {
51575164
body: parser.parse_expr()?,
51585165
}
5166+
} else if parser.parse_keywords(&[Keyword::SET, Keyword::COMMENT]) {
5167+
parser.expect_token(&Token::Eq)?;
5168+
if parser.parse_keyword(Keyword::NULL) {
5169+
crate::ast::AlterRowAccessPolicyOperation::UnsetComment
5170+
} else {
5171+
crate::ast::AlterRowAccessPolicyOperation::SetComment {
5172+
comment: parse_row_access_policy_comment(parser)?,
5173+
}
5174+
}
5175+
} else if parser.parse_keywords(&[Keyword::UNSET, Keyword::COMMENT]) {
5176+
crate::ast::AlterRowAccessPolicyOperation::UnsetComment
51595177
} else {
51605178
parser.expect_keywords(&[Keyword::RENAME, Keyword::TO])?;
51615179
crate::ast::AlterRowAccessPolicyOperation::RenameTo {
@@ -5169,6 +5187,17 @@ fn parse_alter_row_access_policy(parser: &mut Parser) -> Result<Statement, Parse
51695187
})
51705188
}
51715189

5190+
fn parse_row_access_policy_comment(parser: &mut Parser) -> Result<String, ParserError> {
5191+
let unquoted = matches!(parser.peek_token_ref().token,
5192+
Token::Word(ref word) if word.quote_style.is_none());
5193+
let comment = parser.parse_literal_string()?;
5194+
Ok(if unquoted {
5195+
comment.to_uppercase()
5196+
} else {
5197+
comment
5198+
})
5199+
}
5200+
51725201
/// Parse `DROP ROW ACCESS POLICY [IF EXISTS] <name>`
51735202
fn parse_drop_row_access_policy(parser: &mut Parser) -> Result<Statement, ParserError> {
51745203
let if_exists = parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);

0 commit comments

Comments
 (0)