diff --git a/dbt/adapters/duckdb/connections.py b/dbt/adapters/duckdb/connections.py index cbd68dc9..359e153e 100644 --- a/dbt/adapters/duckdb/connections.py +++ b/dbt/adapters/duckdb/connections.py @@ -3,6 +3,7 @@ from contextlib import contextmanager from multiprocessing.context import SpawnContext from typing import Optional +from typing import Set from typing import Tuple from typing import TYPE_CHECKING @@ -25,6 +26,7 @@ class DuckDBConnectionManager(SQLConnectionManager): TYPE = "duckdb" _LOCK = threading.RLock() _ENV = None + _LOGGED_MESSAGES: Set[str] = set() def __init__(self, config: AdapterRequiredConfig, mp_context: SpawnContext) -> None: super().__init__(config, mp_context) @@ -68,6 +70,15 @@ def close(cls, connection: Connection) -> Connection: connection = super(SQLConnectionManager, cls).close(connection) return connection + @classmethod + def warn_once(cls, msg: str): + """Post a warning message once per dbt execution.""" + with cls._LOCK: + if msg in cls._LOGGED_MESSAGES: + return + cls._LOGGED_MESSAGES.add(msg) + logger.warning(msg) + def cancel(self, connection: Connection): if self._ENV is not None: logger.debug( diff --git a/dbt/adapters/duckdb/impl.py b/dbt/adapters/duckdb/impl.py index 89de7873..b24c01db 100644 --- a/dbt/adapters/duckdb/impl.py +++ b/dbt/adapters/duckdb/impl.py @@ -22,6 +22,7 @@ from dbt.adapters.duckdb.relation import DuckDBRelation from dbt.adapters.duckdb.utils import TargetConfig from dbt.adapters.duckdb.utils import TargetLocation +from dbt.adapters.events.logging import AdapterLogger from dbt.adapters.sql import SQLAdapter @@ -31,6 +32,8 @@ if TYPE_CHECKING: import agate +logger = AdapterLogger("DuckDB") + class DuckDBAdapter(SQLAdapter): ConnectionManager = DuckDBConnectionManager @@ -171,6 +174,11 @@ def external_read_location(self, write_location: str, rendered_options: dict) -> return ".".join(["/".join(globs), str(rendered_options.get("format", "parquet"))]) return write_location + @available + def warn_once(self, msg: str): + """Post a warning message once per dbt execution.""" + DuckDBConnectionManager.warn_once(msg) + def valid_incremental_strategies(self) -> Sequence[str]: """DuckDB does not currently support MERGE statement.""" return ["append", "delete+insert"] @@ -255,7 +263,8 @@ def _clean_up_temp_relation_for_incremental(self, config): if self.is_motherduck() and hasattr(config, "model"): if "incremental" == config.model.get_materialization(): temp_relation = self.Relation( - path=self.get_temp_relation_path(config.model), type=RelationType.Table + path=self.get_temp_relation_path(config.model), + type=RelationType.Table, ) self.drop_relation(temp_relation) @@ -278,7 +287,9 @@ def get_temp_relation_path(self, model: Any): table that is dropped at the end of the incremental macro or post-model hook. """ return Path( - schema=self._temp_schema_name, database=model.database, identifier=model.identifier + schema=self._temp_schema_name, + database=model.database, + identifier=model.identifier, ) def post_model_hook(self, config: Any, context: Any) -> None: diff --git a/dbt/include/duckdb/macros/adapters.sql b/dbt/include/duckdb/macros/adapters.sql index 84f2ac0b..29d2d570 100644 --- a/dbt/include/duckdb/macros/adapters.sql +++ b/dbt/include/duckdb/macros/adapters.sql @@ -237,3 +237,10 @@ def materialize(df, con): {% do return(options) %} {%- endmacro %} + +{% macro duckdb__apply_grants(relation, grant_config, should_revoke=True) %} + {#-- If grant_config is {} or None, this is a no-op --#} + {% if grant_config %} + {{ adapter.warn_once('Grants for relations are not supported by DuckDB') }} + {% endif %} +{% endmacro %}