Skip to content

Commit cca5ef5

Browse files
localstack-spiral[bot]spiralsabir-akhadov-localstack
authored
LAV-3234: Support TAG clauses in CREATE SEMANTIC VIEW definitions (#3574)
* LAV-3234: Persist semantic view tag clauses and render tagged DDL Parse view-level TAG, preserve declaration-level table and expression tags, and record each assignment through the shared tag edge lifecycle. Render Cloud-equivalent GET_DDL; preserve assignments through tag and view renames, replacement, and tag deletion. Capture Cloud creation, errors, readback, and DDL roundtrips. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Deviations Cloud accepts values outside a tag's ALLOWED_VALUES on semantic-view tables and expressions, while it rejects them on the view itself. The child-domain assignment path follows the Cloud snapshot. ## Test matrix WITH TAG view, multiple names / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG view / replace -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG table / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments WITH TAG fact / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG dimension / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments WITH TAG metric / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments view / SYSTEM$GET_TAG, TAG_REFERENCES -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments all targets / DESCRIBE, GET_DDL, replay -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments view missing tag / error, no view -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors table missing tag / error, no view -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors view disallowed value / error -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors child disallowed value / accepted -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors failed replacement / old tag kept -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors successful replacement / tags reset -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments ALTER SET, UNSET / readback -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_alter_tag_readback view rename / edge follows -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_alter_tag_readback tag rename and drop / all targets -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_entity_rename value 256 boundary / shared UDF -> tests/queries/test_tags.py::test_create_stream_with_tag value 257 error / shared UDF -> tests/queries/test_tags.py::test_create_stream_tag_failures_are_atomic * LAV-3234: Match quoted semantic-view tag DDL to Cloud Render qualified tag names with identifier quoting and capture the Cloud GET_DDL behavior for a value containing an apostrophe. A plain value on the same quoted tag roundtrips through the emitted DDL. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Deviations Cloud accepts values outside a tag's ALLOWED_VALUES on semantic-view tables and expressions, while rejecting them on the view. Child-domain assignments follow that captured behavior. Cloud GET_DDL emits an embedded apostrophe in a semantic-view tag value without escaping it; replay of that DDL fails with 001003. The captured readback follows Cloud, and a quoted tag with a plain value proves the roundtrip criterion where Cloud emits parseable DDL. ## Test matrix WITH TAG view, multiple names / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG view / replace -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG table / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments WITH TAG fact / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments bare TAG dimension / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments WITH TAG metric / create -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments view / GET_TAG and TAG_REFERENCES -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments all targets / DESCRIBE and GET_DDL -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments all targets / GET_DDL replay -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments view missing tag / error, no view -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors table missing tag / error, no view -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors view disallowed value / error -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors child disallowed value / accepted -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors failed replace / old assignment kept -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_errors successful replace / tags reset -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_assignments ALTER SET, UNSET / readback -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_alter_tag_readback view rename / assignment follows -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_alter_tag_readback tag rename and drop / all targets -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_tag_entity_rename quoted tag / GET_DDL and replay -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_quoted_tag_ddl_roundtrip apostrophe value / Cloud GET_DDL -> tests/queries/ddl/test_semantic_view_tags.py::test_semantic_view_quoted_tag_ddl_roundtrip 256-character value / shared UDF -> tests/queries/test_tags.py::test_create_stream_with_tag 257-character value / shared UDF error -> tests/queries/test_tags.py::test_create_stream_tag_failures_are_atomic --------- Co-authored-by: spiral <spiral@localhost> Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
1 parent c753cf8 commit cca5ef5

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

‎src/ast/ddl.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,6 +1914,8 @@ pub struct CreateSemanticView {
19141914
pub name: ObjectName,
19151915
/// The clause lists in declaration order.
19161916
pub clauses: Vec<SemanticViewClause>,
1917+
/// `[WITH] TAG ( ... )` assignments on the semantic view.
1918+
pub tags: Vec<Tag>,
19171919
/// `COMMENT = '...'`.
19181920
pub comment: Option<String>,
19191921
/// `MAX_STALENESS = '<interval>'` — carried opaquely (ADR 101 §7). Real
@@ -1942,6 +1944,9 @@ impl fmt::Display for CreateSemanticView {
19421944
for clause in &self.clauses {
19431945
write!(f, " {clause}")?;
19441946
}
1947+
if !self.tags.is_empty() {
1948+
write!(f, " WITH TAG ({})", display_comma_separated(&self.tags))?;
1949+
}
19451950
if let Some(comment) = &self.comment {
19461951
write!(f, " COMMENT = '{}'", escape_single_quote_string(comment))?;
19471952
}

‎src/dialect/snowflake.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5544,6 +5544,7 @@ fn parse_create_semantic_view(
55445544
let name = parser.parse_object_name(false)?;
55455545

55465546
let mut clauses = Vec::new();
5547+
let mut tags = Vec::new();
55475548
let mut comment = None;
55485549
let mut max_staleness = None;
55495550
let mut ai_sql_generation = None;
@@ -5593,6 +5594,11 @@ fn parse_create_semantic_view(
55935594
&& consume_semantic_word(parser, "AI_VERIFIED_QUERIES")
55945595
{
55955596
ai_verified_queries = Some(parse_semantic_view_opaque_paren(parser)?);
5597+
} else if tags.is_empty()
5598+
&& (parser.parse_keywords(&[Keyword::WITH, Keyword::TAG])
5599+
|| parser.parse_keyword(Keyword::TAG))
5600+
{
5601+
tags = parse_semantic_view_tag_list(parser)?;
55965602
} else if !copy_grants && parser.parse_keywords(&[Keyword::COPY, Keyword::GRANTS]) {
55975603
copy_grants = true;
55985604
} else {
@@ -5605,6 +5611,7 @@ fn parse_create_semantic_view(
56055611
if_not_exists,
56065612
name,
56075613
clauses,
5614+
tags,
56085615
comment,
56095616
max_staleness,
56105617
ai_sql_generation,

0 commit comments

Comments
 (0)