-- Copyright 2020 Josh Wills. All rights reserved. -- Copyright Materialize, Inc. and contributors. All rights reserved. -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License in the LICENSE file at the -- root of this repository, or online at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- Most of these macros are direct copies of their PostgreSQL counterparts. -- See: https://github.com/dbt-labs/dbt-core/blob/13b18654f/plugins/postgres/dbt/include/postgres/macros/adapters.sql {% macro materialize__create_view_as(relation, sql) -%} create view {{ relation }} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{ get_assert_columns_equivalent(sql) }} {%- endif %} as ( {{ sql }} ); {%- endmacro %} {% macro materialize__create_materialized_view_as(relation, sql) -%} {%- set cluster = adapter.generate_final_cluster_name(config.get('cluster', target.cluster)) -%} create materialized view {{ relation }} {% if cluster %} in cluster {{ cluster }} {% endif %} {# Scheduled refreshes #} {%- set refresh_interval = config.get('refresh_interval') -%} {%- if refresh_interval -%} with ( {{ materialize__get_refresh_interval_sql(relation, refresh_interval) }} ) {%- endif %} {# History retention #} {%- set retain_history = config.get('retain_history') -%} {%- if retain_history -%} with (retain history for '{{ retain_history }}') {%- endif %} {# Contracts and constraints #} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{ get_assert_columns_equivalent(sql) }} {% set ns = namespace(c_constraints=False, m_constraints=False) %} {# Column-level constraints #} {% set raw_columns = model['columns'] %} {% for c_id, c_details in raw_columns.items() if c_details['constraints'] != [] %} {% set ns.c_constraints = True %} {%- endfor %} {# Model-level constraints #} -- NOTE(morsapaes): not_null constraints are not originally supported in -- dbt-core at model-level, since model-level constraints are intended for -- multi-columns constraints. Any model-level constraint is ignored in -- dbt-materialize, albeit silently. {% if model['constraints'] != [] %} {% set ns.m_constraints = True %} {%- endif %} {% if ns.c_constraints %} with {{ get_table_columns_and_constraints() }} {%- endif %} {%- endif %} as ( {{ sql }} ); {%- endmacro %} {% macro materialize__create_arbitrary_object(sql) -%} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{exceptions.warn("Model contracts cannot be enforced for custom materializations (see dbt-core #7213)")}} {%- endif %} {{ sql }} {%- endmacro %} {% macro materialize__create_source(relation, sql) -%} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{exceptions.warn("Model contracts cannot be enforced for custom materializations (see dbt-core #7213)")}} {%- endif %} {%- set cluster = adapter.generate_final_cluster_name(config.get('cluster', target.cluster)) -%} create source {{ relation }} {% if cluster %} in cluster {{ cluster }} {% endif %} {{ sql }} ; {%- endmacro %} {% macro materialize__create_source_table(relation, sql) %} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{exceptions.warn("Model contracts cannot be enforced for custom materializations (see dbt-core #7213)")}} {%- endif %} create table {{ relation }} {{ sql }} {% endmacro %} {% macro materialize__create_sink(relation, sql) -%} {% set contract_config = config.get('contract') %} {% if contract_config.enforced %} {{exceptions.warn("Model contracts cannot be enforced for custom materializations (see dbt-core #7213)")}} {%- endif %} {%- set cluster = adapter.generate_final_cluster_name(config.get('cluster', target.cluster)) -%} create sink {{ relation }} {% if cluster %} in cluster {{ cluster }} {% endif %} {{ sql }} ; {%- endmacro %} {% macro materialize__rename_relation(from_relation, to_relation) -%} {% set target_name = adapter.quote_as_configured(to_relation.identifier, 'identifier') %} {% call statement('rename_relation') -%} {% if relation.type == 'view' %} alter view {{ from_relation }} rename to {{ target_name }} {% else %} alter materialized view {{ from_relation }} rename to {{ target_name }} {% endif %} {%- endcall %} {% endmacro %} {% macro materialize__drop_relation(relation) -%} {% call statement('drop_relation') -%} {% if relation.type == 'view' %} drop view if exists {{ relation }} cascade {% elif relation.is_materialized_view %} drop materialized view if exists {{ relation }} cascade {% elif relation.type == 'sink' %} drop sink if exists {{ relation }} {% elif relation.type == 'source' %} drop source if exists {{ relation }} cascade {% elif relation.type == 'index' %} drop index if exists {{ relation }} -- Tables are not supported as a materialization type in dbt-materialize, -- but seeds and source tables are materialized as tables. {% elif relation.type == 'table' %} drop table if exists {{ relation }} cascade {% endif %} {%- endcall %} {% endmacro %} {% macro set_cluster(cluster) %} set cluster = {{ cluster }}; {% endmacro %} {% macro materialize__truncate_relation(relation) -%} -- Materialize does not support the TRUNCATE command, so we work around that -- by using an unqualified DELETE. {% call statement('truncate_relation') -%} delete from {{ relation }} {%- endcall %} {% endmacro %} {% macro truncate_relation_sql(relation, cluster) -%} -- DELETE requires a scan of the relation, so it needs a valid cluster to run -- against. This is expected to fail if no cluster is specified for the -- target in the materialization configuration, `profiles.yml`, _and_ the -- default cluster for the user is invalid(or intentionally set to -- mz_catalog_server, which cannot query user data). {% if cluster -%} {%- set origin_cluster = adapter.generate_final_cluster_name(cluster) -%} {% do run_query(set_cluster(origin_cluster)) -%} {%- endif %} {{ truncate_relation(relation) }} {% endmacro %} {% macro materialize__get_create_index_sql(relation, index_dict) -%} {%- set index_config = adapter.parse_index(index_dict) -%} {%- set cluster = index_config.cluster or config.get('cluster', target.cluster) -%} {%- set cluster = adapter.generate_final_cluster_name(cluster) -%} create {% if index_config.default -%} default {%- endif %} index {% if index_config.name -%} "{{ index_config.name }}" {%- endif %} {% if cluster -%} in cluster {{ cluster }} {%- endif %} on {{ relation }} {% if index_config.columns -%} ({{ ", ".join(index_config.columns) }}) {%- endif %}; {%- endmacro %} {% macro materialize__persist_docs(relation, model, for_relation, for_columns) -%} {% if for_relation and config.persist_relation_docs() and model.description %} {% do run_query(alter_relation_comment(relation, model.description)) %} {% endif %} {% if for_columns and config.persist_column_docs() and model.columns %} {% set existing_columns = adapter.get_columns_in_relation(relation) | map(attribute="name") | list %} {% set column_dict = model.columns %} -- Materialize does not support running multiple COMMENT ON commands in a -- transaction, so we work around that by forcing a transaction per comment -- instead -- See: https://github.com/MaterializeInc/database-issues/issues/6759 {% for column_name in column_dict if (column_name in existing_columns) %} {% set comment = column_dict[column_name]['description'] %} {% set quote = column_dict[column_name]['quote'] %} {% do run_query(materialize__alter_column_comment_single(relation, column_name, quote, comment)) %} {% endfor %} {% endif %} {% endmacro %} {% macro materialize__apply_grants(relation, grant_config, should_revoke) -%} {{ exceptions.raise_compiler_error( """ dbt-materialize does not implement the grants configuration. If this feature is important to you, please reach out! """ )}} {% endmacro %} {% macro materialize__get_refresh_interval_sql(relation, refresh_interval_dict) -%} {%- set refresh_interval = adapter.parse_refresh_interval(refresh_interval_dict) -%} {% if refresh_interval.at -%} refresh at '{{ refresh_interval.at }}' {%- endif %} {% if refresh_interval.at_creation -%} refresh at creation {%- endif %} {% if refresh_interval.every -%} {% if refresh_interval.at or refresh_interval.at_creation -%} , {%- endif %} refresh every '{{ refresh_interval.every }}' {% if refresh_interval.aligned_to -%} aligned to '{{ refresh_interval.aligned_to }}' {%- endif %} {%- endif %} {% if refresh_interval.on_commit -%} refresh on commit {%- endif %} {%- endmacro %} {% macro materialize__alter_column_comment_single(relation, column_name, quote, comment) %} {% set escaped_comment = postgres_escape_comment(comment) %} comment on column {{ relation }}.{{ adapter.quote(column_name) if quote else column_name }} is {{ escaped_comment }}; {% endmacro %} -- In the dbt-adapter we extend the Relation class to include sinks and indexes {% macro materialize__list_relations_without_caching(schema_relation) %} {% call statement('list_relations_without_caching', fetch_result=True) -%} select d.name as database, s.name as schema, o.name, case when o.type = 'materialized-view' then 'materialized_view' else o.type end as type from mz_objects o left join mz_sources so on o.id = so.id join mz_schemas s on o.schema_id = s.id and s.name = '{{ schema_relation.schema }}' join mz_databases d on s.database_id = d.id and d.name = '{{ schema_relation.database }}' where o.type in ('table', 'source', 'view', 'materialized-view', 'index', 'sink') -- Exclude subsources and progress subsources, which aren't relevant in this -- context and can bork the adapter (see database-issues#6162) and coalesce(so.type, '') not in ('subsource', 'progress') {% endcall %} {{ return(load_result('list_relations_without_caching').table) }} {% endmacro %}