dbt-duckdb

repository·master·Indexed 23 days ago

https://github.com/duckdb/dbt-duckdb

A dbt adapter for DuckDB that enables advanced data workflows, including direct reading/writing of Parquet, CSV, and JSON files. It supports MotherDuck connections, DuckLake partitioning, and a plugin system for external data sources like Excel, Google Sheets, and SQLAlchemy. Features include multiple incremental strategies (append, delete+insert, merge, microbatch), DuckDB Secrets Manager integration, and the ability to attach additional DuckDB, SQLite, or Postgres databases during a run.

Tokens
7.1K
Snippets
24
Records
31
Agent score
30%

What's inside dbt-duckdb

  1. Use Python models in dbt-duckdb

    master

    dbt-duckdb executes Python models in the same process that owns the DuckDB connection (the Python process running dbt).

    Key details:

    • Execution: The .py file is treated as a module and loaded via importlib.
    • Arguments: The model function receives a dbt object (for ref and source calls) and a DuckDBPyConnection object.
    • Data Types: dbt.ref and dbt.source return DuckDB Relation objects. These can be converted to Pandas/Polars DataFrames or Arrow tables.
    • Return Values: The model function can return any object DuckDB can materialize into a table, such as a Pandas/Polars DataFrame, a DuckDB Relation, an Arrow Table, Dataset, RecordBatchReader, or Scanner.
  2. Connect to MotherDuck

    master

    To connect to MotherDuck, set your path to an md:<database> connection string.

    Note: MotherDuck is compatible with client DuckDB versions 0.10.2 and newer. It preloads common extensions but does not support loading custom extensions or user-defined functions.

    default:
      outputs:
        dev:
          type: duckdb
          path: md:my_database
      target: dev
  3. Register upstream external models in memory

    master

    When using :memory: as the DuckDB database, subsequent dbt runs may fail if they depend on external tables, because external files are only registered as DuckDB views when they are created.

    To automatically register these models at the start of a run, add the register_upstream_external_models macro to your on-run-start configuration in dbt_project.yml.

    on-run-start:
      - "{{ register_upstream_external_models() }}"
  4. Read from external files using dbt sources

    master

    You can reference external files (CSV, JSON, Parquet) directly in dbt models or as sources using the external_location property.

    Placement and Behavior:

    • Use meta for external_location if you want the setting to appear in dbt docs generate.
    • Use config for external_location if you want to exclude it from generated documentation.
    • external_location can be a path string (supporting f-string patterns like {name}) or a DuckDB function call (e.g., read_parquet(...)).

    String Formatting Strategies: If your external_location contains characters that conflict with Python string formatting (like curly braces in a function call), use the formatter option:

    • newstyle: Default.
    • oldstyle: Uses % formatting.
    • template: Uses str.format style.

    Example: Using a function call for a source table:

    sources:
      - name: flights_source
        tables:
          - name: flights
            config:
              external_location: "read_csv('flights.csv', types={'FlightDate': 'DATE'}, names=['FlightDate', 'UniqueCarrier'])"
              formatter: oldstyle
    sources:
      - name: external_source
        meta:
          external_location: "s3://my-bucket/my-sources/{name}.parquet"
        tables:
          - name: source1
          - name: source2
            config:
              external_location: "read_parquet(['s3://my-bucket/my-sources/source2a.parquet', 's3://my-bucket/my-sources/source2b.parquet'])"
  5. Use the dbt-duckdb interactive shell

    master

    The interactive shell (available since version 1.9.3) allows you to run dbt commands and query the DuckDB database in an integrated CLI environment. It automatically launches the DuckDB UI for visual data exploration.

    Startup Commands:

    # Start the shell
    python -m dbt.adapters.duckdb.cli
    
    # Start the shell with a specific profile
    python -m dbt.adapters.duckdb.cli --profile my_profile

    Shell Behavior: Upon launch, the shell automatically runs dbt debug, parses the project, and opens the DuckDB UI.

    Supported Commands:

    • run, test, build, seed, snapshot, compile, parse, debug, deps, list

    Model Autocompletion: To enable model name autocompletion in the shell, install the iterfzf package:

    pip install iterfzf
    python -m dbt.adapters.duckdb.cli
  6. Configure incremental strategies

    master

    dbt-duckdb supports several incremental strategies for table models:

    1. append: Appends new records. You can use incremental_predicates to filter which records are appended.
    2. delete+insert: Deletes existing records matching a criteria and inserts new ones.
    3. merge: Uses DuckDB's native MERGE statement (Requires DuckDB >= 1.4.0).
    4. microbatch: Requires dbt-core >= 1.9.

    Example: Append strategy with predicates:

    models:
      - name: my_incremental_model
        config:
          materialized: incremental
          incremental_strategy: append
          incremental_predicates: ["created_at > (select max(created_at) from {{ this }})"]
  7. Configure a minimal dbt-duckdb profile

    master

    A minimal profile uses type: duckdb. By default, this runs against an in-memory DuckDB database (:memory:) which is not persisted after the run completes. This is useful for testing pipelines or operating on external files (CSV, Parquet, JSON) without a persistent database.

    default:
      outputs:
        dev:
          type: duckdb
      target: dev
  8. Write models to external files

    master

    Use the external materialization strategy to create dbt models backed by external files (Parquet, CSV, or JSON).

    Configuration Options:

    • location: The path/filename to write to. If omitted, the file is named after the model. If specified, the format is inferred from the extension (v1.4.1+).
    • format: The file format (parquet, csv, or json). Default is parquet.
    • delimiter: For CSV files, the field delimiter (Default: ,).
    • options: Arbitrary options passed to DuckDB's COPY operation (e.g., partition_by, codec).
    • glue_register: Boolean; if true, registers the file with the AWS Glue Catalog (Default: false).
    • glue_database: The AWS Glue database name.

    Note: Incremental materialization is not supported for external models. Files are created relative to the current working directory unless external_root is set in the DuckDB profile.

    {{ config(materialized='external', location='local/directory/file.parquet') }}
    SELECT m.*, s.id IS NOT NULL as has_source_id
    FROM {{ ref('upstream_model') }} m
    LEFT JOIN {{ source('upstream', 'source') }} s USING (id)
  9. Connect to hosted DuckLake on MotherDuck

    master

    To use hosted DuckLake on MotherDuck, set is_ducklake: true in your profile. DuckLake must be identified so that safe DDL operations are applied by dbt.

    default:
      outputs:
        dev:
          type: duckdb
          path: md:my_ducklake
          is_ducklake: true
      target: dev
  10. Use ~/.duckdbrc for automatic DuckDB configuration

    master
    The duckdbt shell automatically checks for the existence of a ~/.duckdbrc file. If the file exists, the shell reads its contents and executes the SQL commands within the active DuckDB connection during startup. This is useful for setting global DuckDB configurations or extensions automatically when entering the shell.
  11. Configure dbt-duckdb plugins

    master

    dbt-duckdb features a plugin system to extend functionality, such as defining custom Python UDFs or loading data from Excel, Google Sheets, or SQLAlchemy.

    To use a plugin, add it to the plugins property in your dbt profile. Each plugin requires a module property. Built-in plugins (like excel or gsheet) can be referenced by their base filename, while custom plugins must use their full Python module path.

    Plugin Configuration Keys:

    • module: The location of the Plugin class.
    • alias: (Optional) An override for the plugin's name used in logging.
    • config: A dictionary of arbitrary key-value pairs passed to the plugin's initialization.

    Built-in Plugin Dependencies:

    • excel: requires pandas, and openpyxl or xlsxwriter.
    • gsheet: requires gspread and pandas.
    • iceberg: requires pyiceberg and Python >= 3.10.
    • sqlalchemy: requires pandas, sqlalchemy, and the appropriate driver.
    • delta (Experimental): requires deltalake.
    default:
      outputs:
        dev:
          type: duckdb
          path: /tmp/dbt.duckdb
          plugins:
            - module: gsheet
              config:
                method: oauth
            - module: sqlalchemy
              alias: sql
              config:
                connection_url: "{{ env_var('DBT_ENV_SECRET_SQLALCHEMY_URI') }}"
            - module: path.to.custom_udf_module