Package: dbt-clickhouse
dbt-clickhouseThis plugin portsdbtfunctionality toClickhouse.The plugin uses syntax that requires ClickHouse version 22.1 or newer. We do not test older versions of Clickhouse.  We also do not currently test
Replicated tables or the relatedON CLUSTERfunctionality.InstallationUse your favorite Python package manager to install the app from PyPI, e.g.pipinstalldbt-clickhouseSupported featuresTable materializationView materializationIncremental materializationMaterialized View materializations (uses theTOform of MATERIALIZED VIEW, experimental)SeedsSourcesDocs generateTestsSnapshotsMost dbt-utils macros (now included in dbt-core)Ephemeral materializationDistributed table materialization (experimental)Distributed incremental materialization (experimental)ContractsUsage NotesSET Statement WarningIn many environments, using the SET statement to persist a ClickHouse setting across all DBT queries is not reliable
and can cause unexpected failures.  This is particularly true when using HTTP connections through a load balancer that
distributes queries across multiple nodes (such as ClickHouse cloud), although in some circumstances this can also
happen with native ClickHouse connections.  Accordingly, we recommend configuring any required ClickHouse settings in the
"custom_settings" property of the DBT profile as a best practice, instead of relying on a prehook "SET" statement as
has been occasionally suggested.DatabaseThe dbt model relation identifierdatabase.schema.tableis not compatible with Clickhouse because Clickhouse does not support aschema.
So we use a simplified approachschema.table, whereschemais the Clickhouse database. Using thedefaultdatabase is not recommended.Example ProfileDefault values are in brackets:your_profile_name:
  target: dev
  outputs:
    dev:
      type: clickhouse
      schema: [default] # ClickHouse database for dbt models

      # optional
      driver: [http] # http or native.  If not set this will be autodetermined based on port setting
      host: [localhost] 
      port: [8123]  # If not set, defaults to 8123, 8443, 9000, 9440 depending on the secure and driver settings 
      user: [default] # User for all database operations
      password: [<empty string>] # Password for the user
      cluster: [<empty string>] If set, certain DDL/table operations will be executed with the `ON CLUSTER` clause using this cluster. Distributed materializations require this setting to work. See the following ClickHouse Cluster section for more details.
      verify: [True] # Validate TLS certificate if using TLS/SSL
      secure: [False] # Use TLS (native protocol) or HTTPS (http protocol)
      retries: [1] # Number of times to retry a "retriable" database exception (such as a 503 'Service Unavailable' error)
      compression: [<empty string>] Use gzip compression if truthy (http), or compression type for a native connection
      connect_timeout: [10] # Timeout in seconds to establish a connection to ClickHouse
      send_receive_timeout: [300] # Timeout in seconds to receive data from the ClickHouse server
      cluster_mode: [False] # Use specific settings designed to improve operation on Replicated databases (recommended for ClickHouse Cloud)
      use_lw_deletes: [False] Use the strategy `delete+insert` as the default incremental strategy.
      check_exchange: [True] # Validate that clickhouse support the atomic EXCHANGE TABLES command.  (Not needed for most ClickHouse versions)
      local_suffix [_local] # Table suffix of local tables on shards for distributed materializations.
      allow_automatic_deduplication [False] # Enable ClickHouse automatic deduplication for Replicated tables
      custom_settings: [{}] # A dictionary/mapping of custom ClickHouse settings for the connection - default is empty.
      
      # Native (clickhouse-driver) connection settings
      sync_request_timeout: [5] Timeout for server ping
      compress_block_size: [1048576] Compression block size if compression is enabledModel ConfigurationOptionDescriptionDefault if anyengineThe table engine (type of table) to use when creating tablesMergeTree()order_byA tuple of column names or arbitrary expressions. This allows you to create a small sparse index that helps find data faster.tuple()partition_byA partition is a logical combination of records in a table by a specified criterion. The partition key can be any expression from the table columns.sharding_keySharding key determines the destination server when inserting into distributed engine table.  The sharding key can be random or as an output of a hash functionrand())primary_keyLike order_by, a ClickHouse primary key expression.  If not specified, ClickHouse will use the order by expression as the primary keyunique_keyA tuple of column names that uniquely identify rows.  Used with incremental models for updates.inserts_onlyIf set to True for an incremental model, incremental updates will be inserted directly to the target table without creating intermediate table. It has been deprecated in favor of theappendincrementalstrategy, which operates in the same wayincremental_strategyIncremental model update strategy ofdelete+insertorappend.  See the following Incremental Model Strategiesdefaultincremental_predicatesAdditional conditions to be applied to the incremental materialization (only applied todelete+insertstrategysettingsA map/dictionary of "TABLE" settings to be used to DDL statements like 'CREATE TABLE' with this modelquery_settingsA map/dictionary of ClickHouse user level settings to be used withINSERTorDELETEstatements in conjunction with this modelClickHouse ClusterTheclustersetting in profile enables dbt-clickhouse to run against a ClickHouse cluster.Effective Scopeifclusteris set in profile,on_cluster_clausenow will return cluster info for:Database creationView materializationDistributed materializationsModels with Replicated enginestable and incremental materializations with non-replicated engine will not be affected byclustersetting (model would be created on the connected node only).CompatibilityIf a model has been created without aclustersetting, dbt-clickhouse will detect the situation and run all DDL/DML withouton clusterclause for this model.A Note on Model SettingsClickHouse has several types/levels of "settings".  In the model configuration above, two types of these are configurable.settingsmeans theSETTINGSclause used inCREATE TABLE/VIEWtypes of DDL statements, so this is generally settings that are specific to the specific ClickHouse table engine.  The newquery_settingsis use to add aSETTINGSclause to theINSERTandDELETEqueries used for model materialization (including incremental materializations).
There are hundreds of ClickHouse settings, and it's not always clear which is a "table" setting and which is a "user" setting (although the latter are generally
available in thesystem.settingstable.)  In general the defaults are recommended, and any use of these properties should be carefully researched and tested.Known LimitationsEphemeral models/CTEs don't work if placed before the "INSERT INTO" in a ClickHouse insert statement, seehttps://github.com/ClickHouse/ClickHouse/issues/30323.  This
should not affect most models, but care should be taken where an ephemeral model is placed in model definitions and other SQL statements.Incremental Model StrategiesAs of version 1.3.2, dbt-clickhouse supports three incremental model strategies.The Default (Legacy) StrategyHistorically ClickHouse has had only limited support for updates and deletes, in the form of asynchronous "mutations."  To emulate expected dbt behavior,
dbt-clickhouse by default creates a new temporary table containing all unaffected (not deleted, not changed) "old" records, plus any new or updated records,
and then swaps or exchanges this temporary table with the existing incremental model relation.  This is the only strategy that preserves the original relation if something
goes wrong before the operation completes; however, since it involves a full copy of the original table, it can be quite expensive and slow to execute.The Delete+Insert StrategyClickHouse added "lightweight deletes" as an experimental feature in version 22.8.  Lightweight deletes are significantly faster than ALTER TABLE ... DELETE
operations, because they don't require rewriting ClickHouse data parts.  The incremental strategydelete+insertutilizes lightweight deletes to implement
incremental materializations that perform significantly better than the "legacy" strategy.  However, there are important caveats to using this strategy:Lightweight deletes must be enabled on your ClickHouse server using the settingallow_experimental_lightweight_delete=1or you
must setuse_lw_deletes=truein your profile (which will enable that setting for your dbt sessions)Lightweight deletes are now production ready, but there may be performance and other problems on ClickHouse versions earlier than 23.3.This strategy operates directly on the affected table/relation (with creating any intermediate or temporary tables), so if there is an issue during the operation, the
data in the incremental model is likely to be in an invalid stateWhen using lightweight deletes, dbt-clickhouse enabled the settingallow_nondeterministic_mutations.  In some very rare cases using non-deterministic incremental_predicates
this could result in a race condition for the updated/deleted items (and related log messages in the ClickHouse logs).  To ensure consistent results the
incremental predicates should only include sub-queries on data that will not be modified during the incremental materialization.The Append StrategyThis strategy replaces theinserts_onlysetting in previous versions of dbt-clickhouse.  This approach simply appends new rows to the existing relation.
As a result duplicate rows are not eliminated, and there is no temporary or intermediate table.  It is the fastest approach if duplicates are either permitted
in the data or excluded by the incremental query WHERE clause/filter.Additional ClickHouse MacrosModel Materialization Utility MacrosThe following macros are included to facilitate creating ClickHouse specific tables and views:engine_clause-- Uses theenginemodel configuration property to assign a ClickHouse table engine.  dbt-clickhouse uses theMergeTreeengine by default.partition_cols-- Uses thepartition_bymodel configuration property to assign a ClickHouse partition key.  No partition key is assigned by default.order_cols-- Uses theorder_bymodel configuration to assign a ClickHouse order by/sorting key.  If not specified ClickHouse will use an empty tuple() and the table will be unsortedprimary_key_clause-- Uses theprimary_keymodel configuration property to assign a ClickHouse primary key.  By default, primary key is set and ClickHouse will use the order by clause as the primary key.on_cluster_clause-- Uses theclusterprofile property to add anON CLUSTERclause to certain dbt-operations: distributed materializations, views creation, database creation.s3Source Helper MacroThes3sourcemacro simplifies the process of selecting ClickHouse data directly from S3 using the ClickHouse S3 table function.  It works by
populating the S3 table function parameters from a named configuration dictionary (the name of the dictionary must end ins3).  The macro
first looks for the dictionary in the profilevars, and then in the model configuration.  The dictionary can contain any of the following
keys used to populate the parameters of the S3 table function:Argument NameDescriptionbucketThe bucket base url, such ashttps://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi.https://is assumed if no protocol is provided.pathThe S3 path to use for the table query, such as/trips_4.gz.  S3 wildcards are supported.fmtThe expected ClickHouse input format (such asTSVorCSVWithNames) of the referenced S3 objects.structureThe column structure of the data in bucket, as a list of name/datatype pairs, such as['id UInt32', 'date DateTime', 'value String']If not provided ClickHouse will infer the structure.aws_access_key_idThe S3 access key id.aws_secret_access_keyThe S3 secret key.compressionThe compression method used with the S3 objects.  If not provided ClickHouse will attempt to determine compression based on the file name.See theS3 test filefor examples of how to use this macro.Contracts and ConstraintsOnly exact column type contracts are supported.  For example, a contract with a UInt32 column type will fail if the model returns a UInt64 or other integer type.
ClickHouse also supportonlyCHECKconstraints on the entire table/model.  Primary key, foreign key, unique, and column level CHECK constraints are not supported.
(See ClickHouse documentation on primary/order by keys.)Materialized Views (Experimental)Amaterialized_viewmaterialization should be aSELECTfrom an existing (source) table.  The adapter will create a target table with the model name
and a ClickHouse MATERIALIZED VIEW with the name<model_name>_mv.  Unlike PostgreSQL, a ClickHouse materialized view is not "static" (and has
no corresponding REFRESH operation).  Instead, it acts as an "insert trigger", and will insert new rows into the target table using the definedSELECT"transformation" in the view definition on rows inserted into the source table.  See the [test file]
(https://github.com/ClickHouse/dbt-clickhouse/blob/main/tests/integration/adapter/materialized_view/test_materialized_view.py)  for an introductory example
of how to use this functionality.Distributed materializationsNotes:dbt-clickhouse queries now automatically include the settinginsert_distributed_sync = 1in order to ensure that downstream incremental
materialization operations execute correctly.  This could cause some distributed table inserts to run more slowly than expected.Distributed table materializationDistributed table created with following steps:Creates temp view with sql query to get right structureCreate empty local tables based on viewCreate distributed table based on local tables.Data inserts into distributed table, so it is distributed across shards without duplicating.Distributed table model example{{config(materialized='distributed_table',order_by='id, created_at',sharding_key='cityHash64(id)',engine='ReplacingMergeTree')}}selectid,created_at,itemfrom{{source('db','table')}}Generated migrationsCREATETABLEdb.table_localonclustercluster(`id`UInt64,`created_at`DateTime,`item`String)ENGINE=ReplacingMergeTreeORDERBY(id,created_at)SETTINGSindex_granularity=8192;CREATETABLEdb.tableonclustercluster(`id`UInt64,`created_at`DateTime,`item`String)ENGINE=Distributed('cluster','db','table_local',cityHash64(id));Distributed incremental materializationIncremental model based on the same idea as distributed table, the main difficulty is to process all incremental strategies correctly.The Append Strategyjust insert data into distributed table.The Delete+InsertStrategy creates distributed temp table to work with all data on every shard.The Default (Legacy) Strategycreates distributed temp and intermediate tables for the same reason.Only shard tables are replacing, because distributed table does not keep data.
The distributed table reloads only when the full_refresh mode is enabled or the table structure may have changed.Distributed incremental model example{{config(materialized='distributed_incremental',engine='MergeTree',incremental_strategy='append',unique_key='id,created_at')}}selectid,created_at,itemfrom{{source('db','table')}}Generated migrationsCREATETABLEdb.table_localonclustercluster(`id`UInt64,`created_at`DateTime,`item`String)ENGINE=MergeTreeSETTINGSindex_granularity=8192;CREATETABLEdb.tableonclustercluster(`id`UInt64,`created_at`DateTime,`item`String)ENGINE=Distributed('cluster','db','table_local',cityHash64(id));Running TestsThis adapter passes all of dbt basic tests as presented in dbt's official docs:https://docs.getdbt.com/docs/contributing/testing-a-new-adapter#testing-your-adapter.
Usepytest teststo run tests.You can customize the test environment via environment variables. We recommend doing so with the pytestpytest-dotenvplugin combined with root leveltest.envconfiguration file (this file should not be checked into git).  The following environment variables are recognized:DBT_CH_TEST_HOST - Default=localhostDBT_CH_TEST_USER - your ClickHouse username. Default=defaultDBT_CH_TEST_PASSWORD - your ClickHouse password. Default=''DBT_CH_TEST_PORT - ClickHouse client port. Default=8123 (The default is automatically changed to the correct port if DBT_CH_TEST_USE_DOCKER is enabled)DBT_CH_TEST_DB_ENGINE - Database engine used to create schemas.  Defaults to '' (server default)DBT_CH_TEST_USE_DOCKER - Set to True to run clickhouse-server docker image (see tests/docker-compose.yml).  Requires docker-compose. Default=FalseDBT_CH_TEST_CH_VERSION - ClickHouse docker image to use.  Defaults tolatestDBT_CH_TEST_INCLUDE_S3 - Include S3 tests.  Default=False since these are currently dependent on a specific ClickHouse S3 bucket/test datasetDBT_CH_TEST_CLUSTER_MODE - Use the profile valueDBT_CH_TEST_CLUSTER - ClickHouse cluster name, if DBT_CH_TEST_USE_DOCKER set to true, onlytest_replicaandtest_shardis valid (see tests/test_config.xml for cluster settings)Original AuthorClickHouse wants to thank @silentsokolovfor creating this connector and for their valuable contributions.