dbt-hotdata 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. dbt_hotdata-0.2.1/.github/CODEOWNERS +1 -0
  2. dbt_hotdata-0.2.1/.github/workflows/ci.yml +72 -0
  3. dbt_hotdata-0.2.1/.github/workflows/release.yml +83 -0
  4. dbt_hotdata-0.2.1/.gitignore +25 -0
  5. dbt_hotdata-0.2.1/CHANGELOG.md +57 -0
  6. dbt_hotdata-0.2.1/LICENSE +21 -0
  7. dbt_hotdata-0.2.1/PKG-INFO +218 -0
  8. dbt_hotdata-0.2.1/README.md +198 -0
  9. dbt_hotdata-0.2.1/dbt/adapters/hotdata/__init__.py +19 -0
  10. dbt_hotdata-0.2.1/dbt/adapters/hotdata/__version__.py +1 -0
  11. dbt_hotdata-0.2.1/dbt/adapters/hotdata/client.py +384 -0
  12. dbt_hotdata-0.2.1/dbt/adapters/hotdata/column.py +74 -0
  13. dbt_hotdata-0.2.1/dbt/adapters/hotdata/connections.py +185 -0
  14. dbt_hotdata-0.2.1/dbt/adapters/hotdata/credentials.py +154 -0
  15. dbt_hotdata-0.2.1/dbt/adapters/hotdata/impl.py +291 -0
  16. dbt_hotdata-0.2.1/dbt/adapters/hotdata/relation.py +19 -0
  17. dbt_hotdata-0.2.1/dbt/adapters/hotdata/seeds.py +154 -0
  18. dbt_hotdata-0.2.1/dbt/include/hotdata/__init__.py +3 -0
  19. dbt_hotdata-0.2.1/dbt/include/hotdata/dbt_project.yml +5 -0
  20. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/adapters.sql +97 -0
  21. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/materializations/incremental.sql +65 -0
  22. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/materializations/seed.sql +33 -0
  23. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/materializations/snapshot.sql +12 -0
  24. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/materializations/table.sql +28 -0
  25. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/materializations/view.sql +13 -0
  26. dbt_hotdata-0.2.1/dbt/include/hotdata/macros/utils.sql +53 -0
  27. dbt_hotdata-0.2.1/dbt/include/hotdata/profile_template.yml +14 -0
  28. dbt_hotdata-0.2.1/docs/architecture.md +114 -0
  29. dbt_hotdata-0.2.1/pyproject.toml +94 -0
  30. dbt_hotdata-0.2.1/tests/conftest.py +95 -0
  31. dbt_hotdata-0.2.1/tests/test_client.py +224 -0
  32. dbt_hotdata-0.2.1/tests/test_column.py +37 -0
  33. dbt_hotdata-0.2.1/tests/test_connections.py +73 -0
  34. dbt_hotdata-0.2.1/tests/test_credentials.py +139 -0
  35. dbt_hotdata-0.2.1/tests/test_impl.py +159 -0
  36. dbt_hotdata-0.2.1/tests/test_project_parse.py +59 -0
  37. dbt_hotdata-0.2.1/tests/test_project_run.py +119 -0
  38. dbt_hotdata-0.2.1/tests/test_review_fixes.py +102 -0
  39. dbt_hotdata-0.2.1/tests/test_seeds.py +97 -0
  40. dbt_hotdata-0.2.1/uv.lock +1757 -0
@@ -0,0 +1 @@
1
+ * @hotdata-dev/engineers
@@ -0,0 +1,72 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ checks:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install
20
+ run: uv sync
21
+ - name: Lint
22
+ run: uv run ruff check dbt tests
23
+ - name: Format
24
+ run: uv run ruff format --check dbt tests
25
+ - name: Type-check
26
+ run: uv run mypy
27
+ - name: Test (offline)
28
+ run: uv run pytest -q
29
+
30
+ wheel-contents:
31
+ # The adapter ships as PEP 420 namespace packages (dbt/adapters/hotdata +
32
+ # dbt/include/hotdata). A packaging change that drops the macros or config
33
+ # files produces a wheel that imports fine but cannot run a single model —
34
+ # so assert the contents explicitly.
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: astral-sh/setup-uv@v5
39
+ - name: Build
40
+ run: uv build
41
+ - name: Assert wheel contents
42
+ run: |
43
+ python3 - <<'EOF'
44
+ import glob, sys, zipfile
45
+
46
+ wheel = glob.glob("dist/*.whl")[0]
47
+ names = set(zipfile.ZipFile(wheel).namelist())
48
+ required = [
49
+ "dbt/adapters/hotdata/__init__.py",
50
+ "dbt/adapters/hotdata/__version__.py",
51
+ "dbt/adapters/hotdata/client.py",
52
+ "dbt/adapters/hotdata/column.py",
53
+ "dbt/adapters/hotdata/connections.py",
54
+ "dbt/adapters/hotdata/credentials.py",
55
+ "dbt/adapters/hotdata/impl.py",
56
+ "dbt/adapters/hotdata/relation.py",
57
+ "dbt/adapters/hotdata/seeds.py",
58
+ "dbt/include/hotdata/__init__.py",
59
+ "dbt/include/hotdata/dbt_project.yml",
60
+ "dbt/include/hotdata/profile_template.yml",
61
+ "dbt/include/hotdata/macros/adapters.sql",
62
+ "dbt/include/hotdata/macros/materializations/table.sql",
63
+ "dbt/include/hotdata/macros/materializations/incremental.sql",
64
+ "dbt/include/hotdata/macros/materializations/seed.sql",
65
+ "dbt/include/hotdata/macros/materializations/view.sql",
66
+ "dbt/include/hotdata/macros/materializations/snapshot.sql",
67
+ ]
68
+ missing = [name for name in required if name not in names]
69
+ if missing:
70
+ sys.exit(f"wheel {wheel} is missing: {missing}")
71
+ print(f"ok: {wheel} contains all {len(required)} required files")
72
+ EOF
@@ -0,0 +1,83 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - uses: astral-sh/setup-uv@v5
14
+
15
+ - name: Assert tag matches pyproject version
16
+ run: |
17
+ TAG="${GITHUB_REF_NAME#v}"
18
+ VERSION="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
19
+ if [ "$TAG" != "$VERSION" ]; then
20
+ echo "Tag v$TAG does not match pyproject version $VERSION" >&2
21
+ exit 1
22
+ fi
23
+
24
+ - name: Build
25
+ run: uv build
26
+
27
+ - name: Assert wheel contents
28
+ # Same guard as CI: a packaging change that drops the macros or config
29
+ # files produces a wheel that imports fine but cannot run a model.
30
+ run: |
31
+ python3 - <<'EOF'
32
+ import glob, sys, zipfile
33
+
34
+ wheel = glob.glob("dist/*.whl")[0]
35
+ names = set(zipfile.ZipFile(wheel).namelist())
36
+ required = [
37
+ "dbt/adapters/hotdata/__init__.py",
38
+ "dbt/adapters/hotdata/__version__.py",
39
+ "dbt/adapters/hotdata/client.py",
40
+ "dbt/adapters/hotdata/column.py",
41
+ "dbt/adapters/hotdata/connections.py",
42
+ "dbt/adapters/hotdata/credentials.py",
43
+ "dbt/adapters/hotdata/impl.py",
44
+ "dbt/adapters/hotdata/relation.py",
45
+ "dbt/adapters/hotdata/seeds.py",
46
+ "dbt/include/hotdata/__init__.py",
47
+ "dbt/include/hotdata/dbt_project.yml",
48
+ "dbt/include/hotdata/profile_template.yml",
49
+ "dbt/include/hotdata/macros/adapters.sql",
50
+ "dbt/include/hotdata/macros/materializations/table.sql",
51
+ "dbt/include/hotdata/macros/materializations/incremental.sql",
52
+ "dbt/include/hotdata/macros/materializations/seed.sql",
53
+ "dbt/include/hotdata/macros/materializations/view.sql",
54
+ "dbt/include/hotdata/macros/materializations/snapshot.sql",
55
+ ]
56
+ missing = [name for name in required if name not in names]
57
+ if missing:
58
+ sys.exit(f"wheel {wheel} is missing: {missing}")
59
+ print(f"ok: {wheel} contains all {len(required)} required files")
60
+ EOF
61
+
62
+ - uses: actions/upload-artifact@v4
63
+ with:
64
+ name: dist
65
+ path: dist/
66
+ if-no-files-found: error
67
+
68
+ publish:
69
+ needs: build
70
+ runs-on: ubuntu-latest
71
+ environment:
72
+ name: pypi
73
+ url: https://pypi.org/project/dbt-hotdata/
74
+ permissions:
75
+ # Required for PyPI trusted publishing (OIDC) — no API token needed.
76
+ id-token: write
77
+ steps:
78
+ - uses: actions/download-artifact@v4
79
+ with:
80
+ name: dist
81
+ path: dist/
82
+
83
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Environments
10
+ .venv/
11
+ .env
12
+
13
+ # macOS
14
+ .DS_Store
15
+
16
+ # Tooling caches
17
+ .mypy_cache/
18
+ .ruff_cache/
19
+ .pytest_cache/
20
+ .coverage
21
+
22
+ # dbt artifacts from local test projects
23
+ **/target/
24
+ **/dbt_packages/
25
+ logs/
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-09-08
9
+
10
+ ### Added
11
+
12
+ - Ambient-environment fallbacks for profile fields from the platform's own
13
+ `HOTDATA_*` variables (explicit profile values always win): the API key
14
+ from `HOTDATA_API_KEY`, `workspace_id` from `HOTDATA_WORKSPACE`,
15
+ `database_id` from `HOTDATA_DATABASE`, and `api_base_url` from
16
+ `HOTDATA_API_URL`. Resolution reuses the `hotdata_framework.env` helpers,
17
+ so URLs are normalized the same way as every other SDK consumer. A dbt
18
+ project runs with no profile fields beyond `type: hotdata` when the
19
+ environment provides the rest, and orchestrator bridges (e.g.
20
+ hotdata-dlt-destination's dbt bridge) drive the adapter through the same
21
+ contract. A `database_id` adopted from the environment is logged, since it
22
+ retargets the whole build.
23
+
24
+ ### Fixed
25
+
26
+ - `convert_timezone` returned the UTC instant unshifted; it now converts via
27
+ `to_local_time()` (DST-aware, non-UTC sources verified) and defaults a
28
+ falsy `source_tz` to UTC.
29
+
30
+ ### Changed
31
+
32
+ - Docs and comments now use the product branding — HotSQL for the SQL
33
+ surface, Hotdata for engine behavior — and the README documents the
34
+ cross-database macros and the SQL dialect story.
35
+ - Dev lockfile resolves dbt-core 1.12.3 (sqlparse 0.6.0), clearing the
36
+ open Dependabot alerts; the supported floor stays dbt-core 1.10.
37
+
38
+ ## [0.1.0] - 2026-07-27
39
+
40
+ ### Added
41
+
42
+ - Initial dbt adapter for Hotdata managed databases (`type: hotdata`). Models
43
+ run server-side and the results load back with native modes — no local
44
+ engine, no DDL, pure HTTPS.
45
+ - Materializations: `table`, `incremental` (`append`, or `merge` as a native
46
+ upsert by `unique_key`), `seed` (numbers stay exact), `ephemeral`. Views and
47
+ snapshots fail up front with actionable errors.
48
+ - dbt unit tests, data tests, `dbt show`, source freshness, and
49
+ `dbt docs generate`.
50
+ - Id-first database addressing: pin `database_id` in the profile, or let the
51
+ first run create a database and print its id.
52
+ - Cross-database macros for HotSQL: `dateadd`, `datediff`,
53
+ `convert_timezone`.
54
+ - Transient API errors (409/429/5xx) retry for ~42s via the shared
55
+ `hotdata-framework` client; terminal errors fail the node immediately.
56
+ - CI: lint, format, type-check, offline tests (Python 3.11/3.12), and a
57
+ wheel-contents check.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hotdata Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.5
2
+ Name: dbt-hotdata
3
+ Version: 0.2.1
4
+ Summary: dbt adapter for Hotdata instant databases.
5
+ Project-URL: Homepage, https://hotdata.dev
6
+ Project-URL: Repository, https://github.com/hotdata-dev/dbt-hotdata
7
+ Project-URL: Changelog, https://github.com/hotdata-dev/dbt-hotdata/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/hotdata-dev/dbt-hotdata/issues
9
+ Author-email: 669988+eddietejeda@users.noreply.github.com
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: dbt-adapters<2,>=1.16
14
+ Requires-Dist: dbt-common<2,>=1.14
15
+ Requires-Dist: dbt-core<2,>=1.10
16
+ Requires-Dist: hotdata-framework<0.10,>=0.9.0
17
+ Requires-Dist: hotdata<0.9,>=0.8.0
18
+ Requires-Dist: pyarrow>=14
19
+ Description-Content-Type: text/markdown
20
+
21
+ # dbt-hotdata
22
+
23
+ [![Python versions](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://pypi.org/project/dbt-hotdata/)
24
+ [![dbt](https://img.shields.io/badge/dbt-1.10%2B-orange.svg)](https://www.getdbt.com)
25
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
26
+
27
+ Transform data in [Hotdata](https://hotdata.dev) instant databases with [dbt](https://www.getdbt.com).
28
+
29
+ Hotdata is a managed analytics engine speaking [HotSQL](https://www.hotdata.dev/docs/sql) (standard SQL with analytics extensions — if you know Postgres, you already know most of it) with **no DDL surface**: tables are created by loading data, not by `CREATE TABLE`. This adapter embraces that. Every model runs as the Chain pattern, entirely against the API:
30
+
31
+ 1. the model's compiled `SELECT` executes **server-side**,
32
+ 2. the result streams back as Arrow,
33
+ 3. a native load (`replace` / `append` / `upsert`) applies it to the managed table.
34
+
35
+ No local database engine, no driver, no version matching — pure Python over HTTPS. The same project runs unchanged on a laptop, in CI, or in a serverless function.
36
+
37
+ ## Contents
38
+
39
+ - [Requirements](#requirements)
40
+ - [Install](#install)
41
+ - [Quickstart](#quickstart)
42
+ - [How materializations work](#how-materializations-work)
43
+ - [Feature support](#feature-support)
44
+ - [Configuration](#configuration)
45
+ - [How it relates to hotdata-dlt-destination](#how-it-relates-to-hotdata-dlt-destination)
46
+ - [Development](#development)
47
+ - [License](#license)
48
+
49
+ ## Requirements
50
+
51
+ - Python **3.11+**, dbt-core **1.10+** (tested through 1.12)
52
+ - A [Hotdata](https://hotdata.dev) workspace, an API key, and its workspace ID — from your Hotdata dashboard or the [Hotdata CLI](https://github.com/hotdata-dev/sdk-python).
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install dbt-hotdata
58
+ # or
59
+ uv add dbt-hotdata
60
+ ```
61
+
62
+ ## Quickstart
63
+
64
+ **profiles.yml**
65
+
66
+ ```yaml
67
+ my_project:
68
+ target: dev
69
+ outputs:
70
+ dev:
71
+ type: hotdata
72
+ workspace_id: your_workspace_id
73
+ # database_id: db_abc123 # pin after the first run — see below
74
+ schema: public
75
+ threads: 1
76
+ ```
77
+
78
+ Set your API key in the environment (it's a secret; the workspace ID is routing, not a credential):
79
+
80
+ ```bash
81
+ export HOTDATA_API_KEY=your_api_key
82
+ ```
83
+
84
+ **dbt_project.yml** — Hotdata has no views, and dbt's default materialization is `view`, so set the project default to `table`:
85
+
86
+ ```yaml
87
+ models:
88
+ my_project:
89
+ +materialized: table
90
+ ```
91
+
92
+ Then:
93
+
94
+ ```bash
95
+ dbt run
96
+ ```
97
+
98
+ On first run, an instant database labelled `dbt` is created automatically and its **id** is printed:
99
+
100
+ ```
101
+ hotdata: created instant database db_abc123 (name='dbt'). Pin it for future runs by
102
+ setting database_id: db_abc123 in profiles.yml.
103
+ ```
104
+
105
+ Instant databases are addressed by id — Hotdata database names are not unique, so a name can't identify one. Pin `database_id` in the profile to keep building into the same database; without it, each run creates a fresh one (useful for CI or per-branch runs — databases can be set to expire).
106
+
107
+ ### An incremental model
108
+
109
+ ```sql
110
+ -- models/events_rollup.sql
111
+ {{ config(
112
+ materialized='incremental',
113
+ incremental_strategy='merge',
114
+ unique_key='event_id'
115
+ ) }}
116
+
117
+ select event_id, user_id, count(*) as touches, max(occurred_at) as last_seen
118
+ from {{ source('app', 'events') }}
119
+ {% if is_incremental() %}
120
+ where occurred_at > (select max(last_seen) from {{ this }})
121
+ {% endif %}
122
+ group by event_id, user_id
123
+ ```
124
+
125
+ `merge` runs as a **native server-side upsert** matched on `unique_key` — updates matches, inserts the rest, no full-table read. `append` (the default strategy) adds the new rows. First runs and `--full-refresh` load with `replace`.
126
+
127
+ ## How materializations work
128
+
129
+ | Materialization | What happens |
130
+ |---|---|
131
+ | `table` | Model SQL runs server-side → result loads with native `replace`. No temp table, no rename swap (there is no rename). |
132
+ | `incremental` | Same, with `append` (default) or `upsert` (`incremental_strategy: merge` + `unique_key`, composite keys supported). |
133
+ | `seed` | The CSV becomes Arrow (numbers stay exact — integers and decimals, never silently floats), then a `replace` load. `column_types:` are applied as Arrow casts. |
134
+ | `ephemeral` | Standard dbt — inlined into consumers, nothing built. |
135
+ | `view` | ❌ Fails up front: Hotdata has no views. The error tells you to set `+materialized: table`. |
136
+ | `snapshot` | ❌ Fails up front: merges update rows in place, so past versions aren't kept. Keep history with an `append` incremental model. |
137
+
138
+ Tests, `dbt show`, analyses, and source freshness all run as plain SELECTs on the server. `dbt docs generate` builds the catalog from the managed-table API plus Arrow schema probes.
139
+
140
+ Schema evolution is additive and automatic: a model that starts producing a new column just includes it in the next load — existing data is never touched, and types can widen but never silently shrink. (`on_schema_change` is therefore ignored.)
141
+
142
+ ### SQL dialect
143
+
144
+ Write models in [HotSQL](https://www.hotdata.dev/docs/sql). It is Postgres-familiar, so SQL written for Postgres mostly runs unchanged, and the adapter overrides the cross-database macros (`dateadd`, `datediff`, `convert_timezone`) where HotSQL differs. Hotdata's query API also accepts the Postgres, DuckDB, and Snowflake dialects (translated to HotSQL server-side), but this adapter always submits model SQL as native HotSQL.
145
+
146
+ ## Feature support
147
+
148
+ | Feature | Support | Notes |
149
+ |---|:-:|---|
150
+ | `table`, `incremental`, `seed`, `ephemeral` | ✅ | See above |
151
+ | Incremental strategies | ⚠️ | `append`, `merge` (native upsert by `unique_key`). No `delete+insert`, no `microbatch` |
152
+ | `view`, `snapshot` | ❌ | Clear error up front |
153
+ | Tests (generic + singular) | ✅ | Run server-side; `store_failures` supported |
154
+ | `dbt docs generate` | ✅ | Catalog from the managed-table API |
155
+ | Source freshness | ✅ | `loaded_at_field` queries run server-side |
156
+ | Cross-database macros | ✅ | `dateadd`, `datediff`, `convert_timezone` implemented for [HotSQL](https://www.hotdata.dev/docs/sql) (`convert_timezone` is DST-aware) |
157
+ | Hooks (`pre-hook`/`post-hook`, `on-run-*`) | ⚠️ | Run server-side — SELECT-shaped SQL only (no DDL exists) |
158
+ | Python models | ❌ | |
159
+ | Model contracts / constraints | ❌ | No DDL; dbt warns they are unenforced |
160
+ | Grants | ❌ | Ignored with a warning — access is governed by workspace API keys |
161
+ | Transactions | ❌ | `begin`/`commit` are no-ops (Hotdata has no transactions) |
162
+ | Query cancellation | ❌ | An in-flight HTTPS query can't be interrupted client-side |
163
+
164
+ ## Configuration
165
+
166
+ | Profile field | Env variable | Default | Description |
167
+ |---|---|---|---|
168
+ | `api_key` | `HOTDATA_API_KEY` | required | API key (a secret — prefer the env var or `"{{ env_var('HOTDATA_API_KEY') }}"`) |
169
+ | `workspace_id` | `HOTDATA_WORKSPACE` | required | Workspace ID (routing, not a secret) |
170
+ | `database_id` | `HOTDATA_DATABASE` | — | Id of the instant database to build into. **This is how a database is targeted** — names aren't unique. Printed on first-run create; pin it to reuse |
171
+ | `database_name` | — | `dbt` | Display label used **only when creating** a new database (never to look one up) |
172
+ | `schema` | — | `public` | Schema inside the instant database |
173
+ | `create_database_if_missing` | — | `true` | Create a database on first run when no `database_id` is pinned |
174
+ | `api_base_url` | `HOTDATA_API_URL` | `https://api.hotdata.dev` | API endpoint |
175
+ | `max_retries` | — | `8` | Retry budget for transient errors (409/429/5xx). Loads take a catalog-level lock per database; ~42s of linear backoff outlasts a concurrent writer |
176
+ | `retry_backoff_seconds` | — | `1.5` | Initial retry wait (grows linearly) |
177
+ | `threads` | — | `1` | Loads into one database serialize server-side (contention is retried); more threads still help when models spend most of their time in query execution |
178
+
179
+ `database:` stays unset — inside an instant database the SQL catalog is always literally `default` (relations render as `"default"."schema"."table"`), and the adapter rejects any other value up front.
180
+
181
+ Fields left unset in the profile resolve from the platform's own `HOTDATA_*` environment variables (explicit profile values always win). These are the Hotdata CLI conventions — under any orchestrator that sets them, the adapter needs no profile fields at all beyond `type: hotdata`. A `database_id` adopted from the environment is logged, since it retargets the whole build.
182
+
183
+ ## How it relates to hotdata-dlt-destination
184
+
185
+ [hotdata-dlt-destination](https://github.com/hotdata-dev/hotdata-dlt-destination) loads external data **into** Hotdata (the EL); this adapter transforms it **inside** Hotdata (the T). They share the same conventions — `HOTDATA_API_KEY` from the environment, `workspace_id` as a plain parameter, id-first `database_id` addressing, the same retry classification — and the same underlying SDK (`hotdata` + `hotdata-framework`). Point dbt at the `database_id` your dlt pipeline prints, add sources for the loaded tables, and build models on top.
186
+
187
+ ### Running after a dlt load
188
+
189
+ [hotdata-dlt-destination](https://github.com/hotdata-dev/hotdata-dlt-destination) ships a dbt bridge: after a pipeline run, one helper call executes a dbt package against the exact instant database the load just wrote — no profiles.yml to author, credentials and routing reused from the pipeline. See “Transform with dbt” in that repo's README. This adapter itself knows nothing about dlt; the bridge drives it through the `HOTDATA_*` environment contract above.
190
+
191
+ ## Development
192
+
193
+ The project uses [uv](https://docs.astral.sh/uv/) for dependency management.
194
+
195
+ ```bash
196
+ git clone https://github.com/hotdata-dev/dbt-hotdata.git
197
+ cd dbt-hotdata
198
+
199
+ uv sync # install deps (including dev group)
200
+
201
+ uv run pytest # run the test suite (offline — no credentials needed)
202
+ uv run ruff check # lint
203
+ uv run ruff format # format
204
+ uv run mypy # type-check
205
+ ```
206
+
207
+ The test suite runs entirely offline: adapter logic is exercised against an in-memory fake client, and a real `dbt parse` verifies plugin registration and every macro.
208
+
209
+ ## License
210
+
211
+ [MIT](LICENSE) © Hotdata Inc.
212
+
213
+ ## Resources
214
+
215
+ - [Hotdata Python SDK](https://github.com/hotdata-dev/sdk-python) · [hotdata-framework](https://github.com/hotdata-dev/sdk-python-framework)
216
+ - [hotdata-dlt-destination](https://github.com/hotdata-dev/hotdata-dlt-destination)
217
+ - [dbt adapter documentation](https://docs.getdbt.com/docs/connect-adapters)
218
+ - [Changelog](CHANGELOG.md) · [Architecture](docs/architecture.md)