{"id":40947230,"url":"https://github.com/exasol/dbt-exasol","last_synced_at":"2026-05-11T13:11:22.655Z","repository":{"id":42627763,"uuid":"195894971","full_name":"exasol/dbt-exasol","owner":"exasol","description":"Data Build Tool adapter for Exasol","archived":false,"fork":false,"pushed_at":"2026-05-11T04:28:53.000Z","size":3133,"stargazers_count":27,"open_issues_count":8,"forks_count":15,"subscribers_count":4,"default_branch":"master","last_synced_at":"2026-05-11T06:35:41.539Z","etag":null,"topics":["data-builder","data-warehousing","exasol-integration"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exasol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2019-07-08T22:36:29.000Z","updated_at":"2026-05-08T00:23:21.000Z","dependencies_parsed_at":"2026-02-12T16:01:33.878Z","dependency_job_id":null,"html_url":"https://github.com/exasol/dbt-exasol","commit_stats":null,"previous_names":["exasol/dbt-exasol","tglunde/dbt-exasol"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/exasol/dbt-exasol","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exasol%2Fdbt-exasol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exasol%2Fdbt-exasol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exasol%2Fdbt-exasol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exasol%2Fdbt-exasol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exasol","download_url":"https://codeload.github.com/exasol/dbt-exasol/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exasol%2Fdbt-exasol/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32896069,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-builder","data-warehousing","exasol-integration"],"created_at":"2026-01-22T05:05:26.205Z","updated_at":"2026-05-11T13:11:22.626Z","avatar_url":"https://github.com/exasol.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dbt-exasol\n\n![CI](https://github.com/tglunde/dbt-exasol/actions/workflows/ci.yml/badge.svg)\n\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=security_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=reliability_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=sqale_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=sqale_index)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n\n[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=code_smells)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Adbt-exasol\u0026metric=ncloc)](https://sonarcloud.io/dashboard?id=com.exasol%3Adbt-exasol)\n\n**[dbt](https://www.getdbt.com/)** enables data analysts and engineers to transform their data using the same practices that software engineers use to build applications.\n\nPlease see the dbt documentation on **[Exasol setup](https://docs.getdbt.com/reference/warehouse-setups/exasol-setup)** for more information on how to start using the Exasol adapter.\n\n`dbt-exasol` is cleared for production use.\n\n## Version Compatibility\n\n| dbt-exasol | dbt-core | Python    | Exasol            |\n|------------|----------|-----------|-------------------|\n| 1.10.x     | 1.10.x   | 3.10-3.13 | 7.x, 8.x, ≥2025.x |\n| 1.8.x      | 1.8.x    |  3.9-3.12 | 7.x, 8.x          |\n| 1.7.x      | 1.7.x    |  3.8-3.11 | 7.x, 8.x          |\n\n## Development Setup\n\nThis project uses [mise-en-place](https://mise.jdx.dev/) for managing development tools and environment.\n\n### Prerequisites\n\n1. Install mise: [mise.jdx.dev/installing-mise](https://mise.jdx.dev/installing-mise.html)\n2. Add shell activation to your rc file:\n\n   ```bash\n   # For bash (~/.bashrc)\n   eval \"$(mise activate bash)\"\n\n   # For zsh (~/.zshrc)\n   eval \"$(mise activate zsh)\"\n\n   # For fish (~/.config/fish/config.fish)\n   mise activate fish | source\n   ```\n\n### Getting Started\n\n```bash\n# Trust the project configuration (one-time)\nmise trust\n\n# Install development tools (uv, gh, bun, usage)\nmise install\n\n# Sync Python dependencies\nmise run sync\n```\n\n### Available Tasks\n\n| Command | Description |\n|---------|-------------|\n| `mise run test` | Run all tests with coverage (nox -s test:coverage) |\n| `mise run test:unit` | Run unit tests only |\n| `mise run test:integration` | Run integration tests only |\n| `mise run format` | Auto-format code (nox -s format:fix) |\n| `mise run format-check` | Check code formatting without changes |\n| `mise run lint` | Run all linters (code + security) |\n| `mise run check` | Run all checks (format, lint, type) |\n| `mise run sync` | Sync dependencies using uv |\n| `mise run nox` | Run nox sessions directly |\n| `mise run tunnel-start` | Start SSH tunnel to remote Docker host |\n| `mise run tunnel-stop` | Stop SSH tunnel |\n| `mise run tunnel-status` | Check SSH tunnel status |\n| `mise run tunnel-restart` | Restart SSH tunnel |\n\nArguments can be passed to tasks: `mise run nox -- -s test:unit`\n\n### Environment Configuration\n\nSee @mise.toml [env] section for environment variables with default values.\n\n- `.env` - Local overrides (gitignored)\n- Required environment variables (`DBT_DSN`, `DBT_USER`, `DBT_PASS`, etc. as described in @mise.toml)\n- `mise.local.toml` - Developer-specific mise overrides (gitignored)\n\n#### Optional Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `DBT_CONN_POOL_SIZE` | 5 | Number of connections to pre-initialize in the pool for improved test performance |\n\n### Docker SSH Tunnel\n\nTo use a remote Docker host via SSH:\n\n1. **Configure the connection** in `.env`:\n\n   ```bash\n   DOCKER_HOST=ssh://user@remote-host\n   ```\n\n2. **Manage the SSH tunnel** using mise tasks:\n\n   ```bash\n   # Start the SSH tunnel\n   mise run tunnel-start\n\n   # Check tunnel status\n   mise run tunnel-status\n\n   # Stop the tunnel\n   mise run tunnel-stop\n\n   # Restart the tunnel\n   mise run tunnel-restart\n   ```\n\nThe tunnel manager creates a persistent SSH connection that Docker can use for remote operations. It handles:\n\n- Background SSH master connection with control sockets\n- Automatic PID tracking\n- Graceful shutdown and cleanup\n- Connection keepalive (60s intervals)\n\n**Requirements:**\n\n- SSH access to the remote host with key-based authentication\n- SSH keys available in `~/.ssh/` or SSH agent\n- Docker installed on the remote host\n\n**Troubleshooting:**\n\n```bash\n# Check detailed status\nmise run tunnel-status\n\n# View tunnel process\nps aux | grep ssh\n\n# Test Docker connection\ndocker -H ssh://user@remote-host ps\n```\n\n# Current profile.yml settings\n\n\u003cFile name='profiles.yml'\u003e\n\n```yaml\ndbt-exasol:\n  target: dev\n  outputs:\n    dev:\n      type: exasol\n      threads: 1\n      dsn: HOST:PORT\n      user: USERNAME\n      password: PASSWORD\n      dbname: db\n      schema: SCHEMA\n```\n\n## Optional login credentials using OpenID for Exasol SaaS\n\nOpenID login through access_token or refresh_token instead of user+password\n\n## Optional parameters\n\n\u003cul\u003e\n  \u003cli\u003e\u003cstrong\u003econnection_timeout\u003c/strong\u003e: defaults to pyexasol default\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003esocket_timeout\u003c/strong\u003e: defaults to pyexasol default\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003equery_timeout\u003c/strong\u003e: defaults to pyexasol default\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003ecompression\u003c/strong\u003e: default: False\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003eencryption\u003c/strong\u003e: default: True\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003evalidate_server_certificate\u003c/strong\u003e: default: True (requires valid SSL certificate when encryption=True)\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003eprotocol_version\u003c/strong\u003e: default: v3\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003erow_separator\u003c/strong\u003e: default: CRLF for windows - LF otherwise\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003etimestamp_format\u003c/strong\u003e: default: YYYY-MM-DDTHH:MI:SS.FF6\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003epool_size\u003c/strong\u003e: default: None (resolved from dbt \u003ccode\u003ethreads\u003c/code\u003e setting). Maximum number of pooled connections per credentials key. When omitted, the pool size equals the \u003ccode\u003ethreads\u003c/code\u003e value so every thread can reuse a cached connection without creating a new one on each model run.\u003c/li\u003e\n\u003c/ul\u003e\n\n# Known isues\n\n## \u003e=1.8.1 additional parameters\n\nAs of dbt-exasol 1.8.1 it is possible to add new model config parameters for models materialized as table or incremental.\n\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003epartition_by_config\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003edistribute_by_config\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eprimary_key_config\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n- Example table materialization config\n\n```yaml\n{{\n    config(\n        materialized='table',\n        primary_key_config=['\u003ccolumn\u003e','\u003ccolumn2\u003e'],\n        partition_by_config='\u003ccolumn\u003e',\n        distribute_by_config='\u003ccolumn\u003e'\n    )\n}}\n```\n\n---\n\n**NOTE**\nIn case more than one column is used, put them in a list.\n\n---\n\n## \u003e=1.8 license change\n\nAs of dbt-exasol version 1.8 we have decided to switch to Apache License from GPLv3 - to be equal to dbt-core licensing.\n\n## setuptools breaking change\n\nDue to a breaking change in setuptools and a infected dependency from dbt-core, we need to use the following [workaround for poetry install](https://github.com/pypa/setuptools/issues/4519#issuecomment-2255446798).\n\n## Using encryption in Exasol 7 vs. 8\n\nStarting from Exasol 8, encryption is enforced by default. If you are still using Exasol 7 and have trouble connecting, you can disable encryption in profiles.yaml (see optional parameters).\n\n## SSL/TLS Certificate Validation\n\nBy default, dbt-exasol validates SSL/TLS certificates when `encryption=True` (which is the default). This provides secure connections and suppresses PyExasol warnings about certificate validation behavior.\n\n**Default behavior (recommended for production):**\n\n```yaml\noutputs:\n  prod:\n    type: exasol\n    encryption: true  # default\n    validate_server_certificate: true  # default\n    # ... other settings\n```\n\n**For development/testing with self-signed certificates:**\n\n```yaml\noutputs:\n  dev:\n    type: exasol\n    encryption: true\n    validate_server_certificate: false  # Skip cert validation (not recommended for production)\n    # ... other settings\n```\n\n**Alternative for self-signed certificates:** Use the `nocertcheck` fingerprint in the DSN:\n\n```yaml\noutputs:\n  dev:\n    type: exasol\n    dsn: myhost/nocertcheck:8563\n    # ... other settings\n```\n\nFor more information about SSL configuration, see the [PyExasol security documentation](https://exasol.github.io/pyexasol/master/user_guide/configuration/security.html).\n\n## Materialized View \u0026 Clone operations\n\nIn Exasol materialized views and clone operations are not suported. Default behaviour from dbt-core will fail accordingly.\n\n## Null handling in test_utils null safe handling\n\nIn Exasol empty string are NULL. Due to this behaviour and as of [this pull request 7776 published in dbt-core 1.6](https://github.com/dbt-labs/dbt-core/pull/7776),\nseeds in tests that use EMPTY literal to simulate empty string have to be handled with special behaviour in exasol.\nSee fixture for csv in exasol**seeds**data_hash_csv for tests/functional/adapter/utils/test_utils.py::TestHashExasol.\n\n## Model contracts\n\nThe following database constraints are implemented for Exasol:\n\n| Constraint Type | Status        |\n| --------------- | ------------- |\n| check           | NOT supported |\n| not null        | enforced      |\n| unique          | NOT supported |\n| primary key     | enforced      |\n| foreign key     | enforced      |\n\n## \u003e=1.5 Incremental model update\n\nFallback to dbt-core implementation and supporting strategies:\n\n- `append` - Insert new rows\n- `merge` - Update existing rows, insert new rows\n- `delete+insert` - Delete matching rows, insert all rows\n- `microbatch` (new in 1.10) - Process data in time-based batches\n\n### Microbatch Strategy\n\nThe microbatch strategy processes data in time-based batches, enabling:\n\n- Efficient processing of large datasets\n- Support for late-arriving data via `lookback`\n- Sample mode (`--sample`) for development\n\n**Example configuration:**\n\n```sql\n{{ config(\n    materialized='incremental',\n    incremental_strategy='microbatch',\n    event_time='created_at',\n    begin='2024-01-01',\n    batch_size='day',\n    lookback=2\n) }}\nselect * from {{ ref('source_table') }}\n```\n\n**Configuration options:**\n\n| Option | Required | Description |\n|--------|----------|-------------|\n| `event_time` | Yes | Column used for time-based filtering |\n| `begin` | Yes | Start date for initial backfill (YYYY-MM-DD) |\n| `batch_size` | Yes | Size of each batch: `hour`, `day`, `month`, `year` |\n| `lookback` | No | Number of previous batches to reprocess |\n\nSee [dbt Microbatch Documentation](https://docs.getdbt.com/docs/build/incremental-microbatch) for more details.\n\n### Sample Mode\n\nSample mode (`--sample` flag) runs dbt in \"small-data\" mode, building only the N most recent time-based slices of microbatch models. This is useful for:\n\n- Development and testing with representative data\n- Quick iteration without processing full history\n\n**Example usage:**\n\n```bash\n# Process only 2 most recent days\ndbt run --sample=\"2 days\"\n\n# Process most recent week\ndbt run --sample=\"1 week\"\n```\n\n**Requirements:**\n\n- Models using `incremental_strategy='microbatch'`\n- dbt-core 1.10 or later\n\nSee [Sample Mode Documentation](https://docs.getdbt.com/docs/build/sample-flag) for more details.\n\n### Microbatch/Sample Mode Notes (Exasol-specific)\n\n**Timestamp Format:** Exasol requires timestamps without timezone suffix in model definitions:\n\n```sql\n-- Correct (Exasol compatible)\nTIMESTAMP '2024-01-01 10:00:00'\n\n-- Incorrect (will cause parse errors)\nTIMESTAMP '2024-01-01 10:00:00-0'\n```\n\nThe dbt-exasol adapter automatically handles timestamp formatting for microbatch boundaries.\n\n**Batch Processing:**\n\n- Microbatch uses DELETE + INSERT pattern for batch replacement\n- Each batch window is processed as a separate transaction\n- For large datasets, consider `batch_size='day'` over `batch_size='hour'`\n\n## \u003e=1.3 Python model not yet supported - WIP\n\n- Please follow [this pull request](https://github.com/tglunde/dbt-exasol/pull/59)\n\n## Breaking changes with release 1.2.2\n\n- Timestamp format defaults to YYYY-MM-DDTHH:MI:SS.FF6\n\n## SQL functions compatibility\n\n### split_part\n\nThere is no equivalent SQL function in Exasol for split_part.\n\n### listagg part_num\n\nThe SQL function listagg in Exasol does not support the num_part parameter.\n\n## Utilities shim package\n\nIn order to support packages like dbt-utils and dbt-audit-helper, we needed to create the [shim package exasol-utils](https://github.com/exasol/dbt-exasol-utils).\n\n# Development\n\n## CI/CD\n\nThis project uses GitHub Actions for continuous integration and deployment:\n\n- **CI Workflow**: Runs on pull requests, pushes to main/master, scheduled nightly, and manual dispatch\n  - **Smart Integration Testing**: Only runs integration tests when relevant files change (on PRs)\n  - **Python Matrix**: Tests across Python 3.10, 3.11, 3.12, and 3.13\n  - **Checks Job** (runs for all Python versions):\n    - Format checking (`nox -s format:check`)\n    - Linting (`nox -s lint:code`)\n    - Security checks (`nox -s lint:security`)\n    - Type checking (`nox -s lint:typing`)\n    - Unit tests with coverage reporting (`nox -s test:unit`)\n  - **Integration Job** (parallel execution with 8 workers):\n    - Functional tests against Exasol database (`nox -s test:integration`)\n    - Conditional execution based on file changes\n  - **Report Job**:\n    - Combines coverage from all jobs\n    - SonarCloud integration for quality gates and coverage reporting\n  - **Concurrency Control**: Cancels redundant runs on the same branch\n\n- **Release Workflow**: Triggered by version tags\n  - Builds package using `uv build`\n  - Publishes to PyPI\n  - Creates GitHub Release\n\n## Local Development Commands\n\nThe following commands are available via mise:\n\n```bash\n# Run format check\nmise run format-check\n\n# Auto-format code\nmise run format\n\n# Run all linters\nmise run lint\n\n# Run unit tests\nmise run test:unit\n\n# Run integration tests\nmise run test:integration\n\n# Run all tests with coverage\nmise run test\n\n# Run all checks (format, lint, type)\nmise run check\n\n# Run specific nox sessions\nmise run nox -- -s test:unit\nmise run nox -- -s lint:security\n```\n\n## Branch Protection\n\nMaintainers should configure the following branch protection rules on the `main` branch:\n\n1. Go to Settings \u003e Branches \u003e Add rule\n2. Branch name pattern: `main`\n3. Enable:\n   - Require a pull request before merging\n   - Require status checks to pass before merging\n   - Select \"test\" as required status check\n   - Require branches to be up to date before merging\n\n## Release Process\n\nTo create a new release:\n\n1. Update version in `pyproject.toml`\n2. Commit the change\n3. Create and push a version tag with `v` prefix:\n\n   ```bash\n   git tag v1.10.2\n   git push origin v1.10.2\n   ```\n\n4. GitHub Actions will automatically:\n   - Build the package\n   - Publish to PyPI\n   - Create a GitHub Release\n\n**Note**: Only semantic version tags with `v` prefix (e.g., `v1.10.2`) trigger releases.\n\n## Code Quality Requirements\n\n- All code must pass format checks (`ruff check`)\n- All code must pass linting (`nox -s lint:code`)\n- Unit test coverage must be \u003e= 80%\n- All tests must pass before merging\n\n# Reporting bugs and contributing code\n\n- Please report bugs using the issues\n- All changes to main must go through pull requests with CI checks passing\n\n# Releases\n\n[GitHub Releases](https://github.com/tglunde/dbt-exasol/releases)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexasol%2Fdbt-exasol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexasol%2Fdbt-exasol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexasol%2Fdbt-exasol/lists"}