Adapters
Overview
Supported database engines and their connection configuration.
SQLBuild uses adapters to connect to different database engines. Snowflake is the main target.
- Supported: ready for production use.
- Beta: builds, tests and plans work, but with less production use so far. Expect rough edges, and please report issues.
| Adapter | Status | Install |
|---|---|---|
| Snowflake | Supported | sqlbuild[snowflake] |
| DuckDB | Supported | included by default |
| MotherDuck | Supported | included by default (uses DuckDB) |
| PostgreSQL | Supported | sqlbuild[postgres] |
| BigQuery | Beta | sqlbuild[bigquery] |
| Databricks | Beta | sqlbuild[databricks] |
| SQL Server | Beta | sqlbuild[sqlserver] |
| SQLite | Beta | included by default |
| ClickHouse | Beta | sqlbuild[clickhouse] |
| Redshift | Planned | |
| Trino | Planned | |
| Spark | Planned | |
| Athena | Planned |
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
Section titled “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
Section titled “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:
from sqlbuild.adapters.duckdb.classes.duckdb_adapter 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=connection, sql="SET enable_progress_bar = true") return connectionadapter = "duckdb_plus"A subclass inherits the parent’s capabilities. For example, an adapter extending the Snowflake
adapter keeps supports_session_warehouse = True, so it can use
command-group warehouses and
--warehouse. An adapter written from scratch that sets this attribute receives the selected
warehouse as the connection’s warehouse key.
Writing an adapter from scratch
Section titled “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:
from sqlbuild.adapter.contract.classes.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.
A few StrictAdapter methods are optional and have a documented default. create_sql_test_output and drop_sql_test_output let an adapter store a unit test’s actual output once, in a temporary relation, so every comparison of that test reads it instead of recomputing the model chain. By default create_sql_test_output returns None, and SQLBuild runs the test’s inline comparison query instead, so adapters that do not implement them keep working unchanged. render_create_temporary_function returns the call target and DDL for a session-scoped copy of a project function, which sqb test calls instead of the built function (see Functions in sqb test). By default it returns None, and tests call the built function. render_drop_schema returns the statements that drop a schema and everything in it, which sqb review uses to remove the schema it built into. By default it raises an error, and sqb review is unavailable for that adapter.
Discovery rules
Section titled “Discovery rules”- SQLBuild discovers all
.pyfiles underadapters/recursively (excluding__init__.pyand files starting with_) - Each file is scanned for classes that define a string
adapter_nameand subclassStrictAdapter(or any of its subclasses likeBaseAdapteror 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,motherduck,snowflake,bigquery,databricks,postgres,sqlite,sqlserver,clickhouse)
Adapter class hierarchy
Section titled “Adapter class hierarchy”StrictAdapter (fully abstract - all methods must be implemented) └── BaseAdapter (ANSI SQL defaults - override only what differs) ├── DuckDbAdapter ├── MotherDuckAdapter ├── SnowflakeAdapter ├── BigQueryAdapter ├── DatabricksAdapter ├── PostgresAdapter ├── SqlServerAdapter └── ClickHouseAdapterStrictAdapter composes four mixins that define the full adapter contract:
- ConnectionMixin -
connect,close,begin,commit,rollback - SchemaMixin -
relation_exists,list_relations,get_columns,ensure_schema - MaterializationMixin -
create_table_as,create_view_as,drop,rename,swap,load_seed - DiffMixin -
diff_schema,diff_rows,sample_unequal_rows,sample_side_only_rows,diff_unkeyed_rows