Skip to main content
SQLBuild uses adapters to connect to different database engines.
AdapterStatusInstall
DuckDBSupportedincluded by default
MotherDuckSupportedincluded by default (uses DuckDB)
SnowflakeSupportedsqlbuild[snowflake]
BigQuerySupportedsqlbuild[bigquery]
DatabricksSupportedsqlbuild[databricks]
PostgreSQLSupportedsqlbuild[postgres]
SQL ServerSupportedsqlbuild[sqlserver]
ClickHouseComing soon
RedshiftComing soon
TrinoComing soon
SparkComing soon
AthenaComing soon
Set the adapter in sqlbuild_project.toml:
name = "my_project"
adapter = "duckdb"
Or override it per developer in sqlbuild_local.toml:
adapter = "duckdb"

Custom adapters

You can write your own adapter for any database engine by placing Python files under adapters/ in your project directory.

Extending a built-in adapter

The most common case is extending an existing adapter with custom behavior. Subclass the built-in adapter, set a new adapter_name, and override what you need:
# adapters/duckdb_plus.py
from sqlbuild.integrations.duckdb.client import DuckDbAdapter

class DuckDbPlusAdapter(DuckDbAdapter):
    adapter_name = "duckdb_plus"

    def connect(self, config):
        connection = super().connect(config)
        # custom setup - load extensions, set pragmas, etc.
        self.execute(connection, "SET enable_progress_bar = true")
        return connection
adapter = "duckdb_plus"

Writing an adapter from scratch

For a database engine with no built-in support, subclass BaseAdapter. It provides ANSI SQL defaults for most methods - you only need to implement connect, execute, close, and any methods where your engine differs from standard SQL:
# adapters/my_database.py
from sqlbuild.adapter.base.base_adapter import BaseAdapter

class MyDatabaseAdapter(BaseAdapter):
    adapter_name = "my_database"
    sql_analysis_dialect_name = "postgres"  # SQL dialect for validation and lineage

    def connect(self, config):
        ...

    def execute(self, connection, sql):
        ...

    def close(self, connection):
        ...
Set sql_analysis_dialect_name to the SQL dialect name that matches your engine’s SQL syntax. This enables compile-time SQL validation, column inference, column lineage, and local scenario replay for your adapter. If omitted, SQLBuild uses generic SQL parsing. SQL analysis is powered by Polyglot, a Rust reimplementation of SQLGlot supporting 32+ dialects. For full control with no inherited defaults, subclass StrictAdapter instead. Every method is abstract and must be implemented explicitly. SQLBuild raises a clear error listing any unimplemented methods.

Discovery rules

  • SQLBuild discovers all .py files under adapters/ recursively (excluding __init__.py and files starting with _)
  • Each file is scanned for classes that define a string adapter_name and subclass StrictAdapter (or any of its subclasses like BaseAdapter or a built-in adapter)
  • Adapter names must be unique across all adapter files - duplicates raise an error
  • Custom adapter names cannot shadow built-in names (duckdb, snowflake, bigquery, databricks, postgres, sqlserver)

Adapter class hierarchy

StrictAdapter          (fully abstract - all methods must be implemented)
  └── BaseAdapter      (ANSI SQL defaults - override only what differs)
        ├── DuckDbAdapter
        ├── SnowflakeAdapter
        ├── BigQueryAdapter
        ├── DatabricksAdapter
        ├── PostgresAdapter
        └── SqlServerAdapter
StrictAdapter composes four mixins that define the full adapter contract:
  • ConnectionMixin - connect, close, begin, commit, rollback
  • SchemaMixin - relation_exists, list_relations, describe_relation, ensure_schema
  • MaterializationMixin - create_table_as, create_view, drop, rename, swap, load_seed
  • DiffMixin - diff_schema, diff_rows, sample_unequal_rows, sample_side_only_rows