diff --git a/.changes/unreleased/Fixes-20240715-143507.yaml b/.changes/unreleased/Fixes-20240715-143507.yaml new file mode 100644 index 000000000..e3dd2f0bf --- /dev/null +++ b/.changes/unreleased/Fixes-20240715-143507.yaml @@ -0,0 +1,6 @@ +kind: Fixes +body: Fix materialized views comment syntax +time: 2024-07-15T14:35:07.412274+12:00 +custom: + Author: jeremyyeo + Issue: "837" diff --git a/dbt/include/redshift/macros/adapters.sql b/dbt/include/redshift/macros/adapters.sql index 5da047f58..8b74933bd 100644 --- a/dbt/include/redshift/macros/adapters.sql +++ b/dbt/include/redshift/macros/adapters.sql @@ -276,9 +276,24 @@ {% endif %} {% endmacro %} +{# + Copied from the postgres-adapter. +#} +{% macro escape_comment(comment) -%} + {% if comment is not string %} + {% do exceptions.raise_compiler_error('cannot escape a non-string: ' ~ comment) %} + {% endif %} + {%- set magic = '$dbt_comment_literal_block$' -%} + {%- if magic in comment -%} + {%- do exceptions.raise_compiler_error('The string ' ~ magic ~ ' is not allowed in comments.') -%} + {%- endif -%} + {{ magic }}{{ comment }}{{ magic }} +{%- endmacro %} {% macro redshift__alter_relation_comment(relation, comment) %} - {% do return(postgres__alter_relation_comment(relation, comment)) %} + {%- set escaped_comment = escape_comment(comment) -%} + {%- set relation_type = 'view' if relation.type == 'materialized_view' else relation.type -%} + comment on {{ relation_type }} {{ relation }} is {{ escaped_comment }}; {% endmacro %} diff --git a/tests/functional/adapter/test_persist_docs.py b/tests/functional/adapter/test_persist_docs.py index 6eeaf881c..cb5e632b6 100644 --- a/tests/functional/adapter/test_persist_docs.py +++ b/tests/functional/adapter/test_persist_docs.py @@ -1,6 +1,7 @@ import json import pytest +from dbt.tests.adapter.materialized_view import files from dbt.tests.util import run_dbt from dbt.tests.adapter.persist_docs.test_persist_docs import ( @@ -10,6 +11,21 @@ BasePersistDocsCommentOnQuotedColumn, ) +_MATERIALIZED_VIEW_PROPERTIES__SCHEMA_YML = """ +version: 2 +models: + - name: my_materialized_view + description: | + Materialized view model description "with double quotes" + and with 'single quotes' as welll as other; + '''abc123''' + reserved -- characters + 80% of statistics are made up on the spot + -- + /* comment */ + Some $lbl$ labeled $lbl$ and $$ unlabeled $$ dollar-quoting +""" + class TestPersistDocs(BasePersistDocs): @pytest.mark.flaky @@ -62,3 +78,31 @@ def test_comment_on_late_binding_view(self, project): no_docs_node = catalog_data["nodes"]["model.test.no_docs_model"] self._assert_has_view_comments(no_docs_node, False, False) + + +class TestPersistDocsWithMaterializedView(BasePersistDocs): + @pytest.fixture(scope="class", autouse=True) + def seeds(self): + return {"my_seed.csv": files.MY_SEED} + + @pytest.fixture(scope="class") + def models(self): + return { + "my_materialized_view.sql": files.MY_MATERIALIZED_VIEW, + } + + @pytest.fixture(scope="class") + def properties(self): + return { + "schema.yml": _MATERIALIZED_VIEW_PROPERTIES__SCHEMA_YML, + } + + @pytest.mark.flaky + def test_has_comments_pglike(self, project): + run_dbt(["docs", "generate"]) + with open("target/catalog.json") as fp: + catalog_data = json.load(fp) + assert "nodes" in catalog_data + assert len(catalog_data["nodes"]) == 2 + view_node = catalog_data["nodes"]["model.test.my_materialized_view"] + assert view_node["metadata"]["comment"].startswith("Materialized view model description")