@@ -620,20 +620,8 @@ def test_format_model_expressions_macro_property_comments_preserved_with_dialect
620620@pytest .mark .parametrize ("dialect" , ["bigquery" , "duckdb" , "snowflake" ])
621621@pytest .mark .parametrize ("prop_name" , ["tags" , "ignored_rules" ])
622622def test_format_model_expressions_list_property_array_literal (dialect : str , prop_name : str ):
623- """Dialect-agnostic header properties that hold a list (`tags`, `ignored_rules`) must
624- render as a bracketed list literal (`[a, b]`) on dialects that spell arrays that way,
625- not the base generator's `ARRAY(a, b)`.
626-
627- On BigQuery, `ARRAY(` is parsed as a subquery constructor, so a multi-element
628- `ARRAY('C1', 'c2')` fails to reparse with `Required keyword: 'value' missing for
629- Property`. This previously affected any dialect using this generator, since these
630- properties render generically (the `dialect=None` path) regardless of the model's
631- own dialect. Only bigquery/duckdb/snowflake-like dialects are covered here;
632- dialects whose own array syntax is not brackets (postgres' `ARRAY[...]`,
633- databricks' `ARRAY(...)`) or that reuse `[`/`]` for identifier quoting (tsql,
634- sqlite, ...) are covered by
635- `test_format_model_expressions_list_property_dialects_without_bracket_arrays`.
636- """
623+ """List-valued header properties render as `[a, b]`, not `ARRAY(a, b)`, which
624+ BigQuery parses as a subquery and fails to load."""
637625 source = f"""MODEL (
638626 name a.b,
639627 dialect { dialect } ,
@@ -642,14 +630,9 @@ def test_format_model_expressions_list_property_array_literal(dialect: str, prop
642630SELECT 1 AS x"""
643631
644632 formatted = format_model_expressions (parse (source , default_dialect = dialect ), dialect = dialect )
645-
646633 assert f"{ prop_name } ['C1', 'c2']" in formatted
647634
648- # Reparses cleanly with the model's own dialect.
649- reparsed = parse (formatted , default_dialect = dialect )
650-
651- # Idempotent: formatting an already-formatted model is a no-op.
652- twice = format_model_expressions (reparsed , dialect = dialect )
635+ twice = format_model_expressions (parse (formatted , default_dialect = dialect ), dialect = dialect )
653636 assert formatted == twice
654637
655638 model = load_sql_based_model (parse (formatted , default_dialect = dialect ), dialect = dialect )
@@ -660,37 +643,20 @@ def test_format_model_expressions_list_property_array_literal(dialect: str, prop
660643
661644
662645def test_format_model_expressions_array_property_no_dialect_unchanged ():
663- """Regression guard: with no model dialect, list-valued header properties must keep
664- rendering through the base generator (`ARRAY(...)`), exactly as pinned by
665- `test_format_model_expressions`. The `[...]` rewrite only applies once a model
666- dialect is present (gated on `meta_dialect`)."""
667646 formatted = format_model_expressions (
668647 parse ("MODEL (name a.b, tags ['C1', 'c2']); SELECT 1 AS x" )
669648 )
670649
671650 assert "tags ARRAY('C1', 'c2')" in formatted
672651
673652
674- @pytest .mark .parametrize ("dialect" , ["tsql" , "sqlite" , "postgres" , "databricks" ])
653+ @pytest .mark .parametrize ("dialect" , ["tsql" , "sqlite" ])
675654@pytest .mark .parametrize ("prop_name" , ["tags" , "ignored_rules" ])
676- def test_format_model_expressions_list_property_dialects_without_bracket_arrays (
655+ def test_format_model_expressions_list_property_bracket_identifier_dialects (
677656 dialect : str , prop_name : str
678657):
679- """Regression: dialects whose own generator does not spell an `exp.Array` as
680- `[a, b]` must NOT get the bracket-list rewrite from
681- `test_format_model_expressions_list_property_array_literal`, and must keep the
682- generic `ARRAY(...)` form.
683-
684- This matters most for tsql and sqlite (also true of tableau, exasol, fabric), which
685- reuse `[`/`]` for identifier quoting: `['a', 'b']` is not an array literal in their
686- grammar at all, so rewriting `tags` or `ignored_rules` to that form reparses as a
687- single bracket-quoted identifier, silently collapsing two values into one and
688- corrupting the tag/rule names -- even though this exact source formatted correctly
689- on `main` before bracket rendering was introduced. postgres (`ARRAY[...]`) and
690- databricks (`ARRAY(...)`) are not corrupted by the bracket form, but should still
691- keep rendering with their own generator's spelling rather than a generic bracket
692- literal that is not how either dialect writes arrays.
693- """
658+ """These dialects quote identifiers with `[...]`, so a bracketed list would reload
659+ as a single identifier. List properties must keep the `ARRAY(...)` form."""
694660 source = f"""MODEL (
695661 name a.b,
696662 dialect { dialect } ,
@@ -699,13 +665,9 @@ def test_format_model_expressions_list_property_dialects_without_bracket_arrays(
699665SELECT 1 AS x"""
700666
701667 formatted = format_model_expressions (parse (source , default_dialect = dialect ), dialect = dialect )
668+ assert f"{ prop_name } ARRAY('C1', 'c2')" in formatted
702669
703- prop_line = formatted .split (f"{ prop_name } " )[1 ].split ("\n " )[0 ]
704- assert prop_line .startswith ("ARRAY" )
705- assert "[" not in prop_line
706-
707- reparsed = parse (formatted , default_dialect = dialect )
708- twice = format_model_expressions (reparsed , dialect = dialect )
670+ twice = format_model_expressions (parse (formatted , default_dialect = dialect ), dialect = dialect )
709671 assert formatted == twice
710672
711673 model = load_sql_based_model (parse (formatted , default_dialect = dialect ), dialect = dialect )
@@ -717,12 +679,8 @@ def test_format_model_expressions_list_property_dialects_without_bracket_arrays(
717679
718680@pytest .mark .parametrize ("dialect" , ["bigquery" , "duckdb" , "snowflake" , "postgres" ])
719681def test_format_model_expressions_grain_alias_render_policy (dialect : str ):
720- """`grain` is renamed to `grains` in `ModelMeta._pre_root_validator`, not via a
721- Pydantic alias, so `_meta_render_policy` must special-case it to inherit `grains`'
722- render policy (warehouse SQL). Otherwise a multi-column `grain [id, id2]` falls back
723- to the generic, dialect-agnostic path and (via the base generator) becomes
724- `ARRAY(id, id2)`, which fails to reparse on BigQuery.
725- """
682+ """`grain` is renamed to `grains` before validation, so it must share the
683+ `grains` render policy instead of falling back to `ARRAY(...)`."""
726684 source = f"""MODEL (
727685 name a.b,
728686 dialect { dialect } ,
@@ -731,29 +689,18 @@ def test_format_model_expressions_grain_alias_render_policy(dialect: str):
731689SELECT 1 AS x, 2 AS id, 3 AS id2"""
732690
733691 formatted = format_model_expressions (parse (source , default_dialect = dialect ), dialect = dialect )
734-
735- # Rendered with the model's own dialect (e.g. postgres' native `ARRAY[...]`), never
736- # the base generator's `ARRAY(id, id2)`, which fails to reparse on BigQuery.
737692 assert "ARRAY(id, id2)" not in formatted
738693
739694 twice = format_model_expressions (parse (formatted , default_dialect = dialect ), dialect = dialect )
740695 assert formatted == twice
741696
742- # `grain [id, id2]` parses to a single composite grain wrapping both columns
743- # (independent of this fix); what matters here is that it survives a dialect-
744- # specific round trip rather than being flattened to the generic `ARRAY(...)`.
745697 model = load_sql_based_model (parse (formatted , default_dialect = dialect ), dialect = dialect )
746- assert len (model .grains ) == 1
747698 assert {c .name for c in model .grains [0 ].find_all (exp .Column )} == {"id" , "id2" }
748699
749700
750701def test_format_model_expressions_table_properties_alias_render_policy ():
751- """`table_properties` is the deprecated alias for `physical_properties`, renamed in
752- `ModelMeta._pre_root_validator`, not via a Pydantic alias. It must inherit
753- `physical_properties`' render policy (warehouse SQL) so dialect-specific values
754- inside it, such as tsql's `DATETIME2`, are not flattened to the generic generator's
755- `TIMESTAMP` spelling.
756- """
702+ """`table_properties` is renamed to `physical_properties` before validation, so it
703+ must keep dialect-specific types such as tsql's `DATETIME2`."""
757704 formatted = format_model_expressions (
758705 parse (
759706 """
0 commit comments