How to Add Support for a New Database Dialect in Superset's db_engine_specs: A Complete Guide
To add support for a new database dialect in Apache Superset, create a Python module in superset/db_engine_specs/ containing a class that inherits from BaseEngineSpec, define the required engine attributes and metadata, and Superset's autodiscovery system will automatically register the dialect without any manual registration steps.
Adding support for a new database dialect allows Apache Superset to generate dialect-specific SQL, handle connection parameters, and expose database-specific features in the UI. This process leverages the db_engine_specs plugin architecture, where each database backend is represented by an engine spec class that defines how Superset interacts with that specific SQL dialect.
Understanding the db_engine_specs Architecture
Superset discovers database-specific behavior through engine spec classes that inherit from BaseEngineSpec (defined in superset/db_engine_specs/base.py at line 324). The base class provides default implementations for SQL generation, schema inspection, and error handling, while individual dialects override specific methods to customize behavior.
The autodiscovery mechanism relies on two key functions in superset/db_engine_specs/__init__.py:
load_engine_specs()(line 62): Iterates through all modules insuperset/db_engine_specs/usingpkgutil.iter_modulesand imports any class that satisfiesis_engine_spec()get_engine_spec(backend, driver)(line 89): Looks up the appropriate spec by callingsupports_backend()on each loaded class until it finds a match
This means you only need to create the spec file and define the class correctly—no manual registration is required.
Step-by-Step Implementation Guide
Step 1: Create the Engine Spec Module
Create a new Python file under superset/db_engine_specs/. The filename is arbitrary (e.g., mydb.py), but the class inside must inherit from BaseEngineSpec or a mixin like BasicParametersMixin.
Ensure the file includes the Apache license header:
# superset/db_engine_specs/mydb.py
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
Step 2: Define the Core Engine Spec Class
Define the class with the required identification attributes. The engine attribute must match the SQLAlchemy dialect name (the string before :// in the connection URL).
from __future__ import annotations
from superset.db_engine_specs.base import BaseEngineSpec, DBEngineSpecMetadata
class MyDBEngineSpec(BaseEngineSpec):
"""Engine spec for MyDB – a fictional column‑store database."""
# Identification
engine = "mydb" # SQLAlchemy backend name
engine_name = "MyDB" # Display name in UI
engine_aliases = {"mydb_legacy"} # Optional alternate names
drivers = {"mydriver": "MyDB driver"} # Map driver name → description
default_driver = "mydriver"
# Connection string placeholder shown in UI
sqlalchemy_uri_placeholder = (
"mydb+mydriver://user:password@host:port/dbname[?key=value]"
)
The supports_backend method in BaseEngineSpec checks these attributes to determine if this spec handles a given connection URL.
Step 3: Add Documentation Metadata
Populate the metadata attribute with a DBEngineSpecMetadata TypedDict. This centralizes UI-display information and eliminates the need to touch multiple configuration files.
metadata: DBEngineSpecMetadata = {
"description": "MyDB is a fast column‑store DB for analytical workloads.",
"logo": "mydb.svg",
"homepage_url": "https://mydb.example.com/",
"categories": ["Analytical Databases", "Open Source"],
"pypi_packages": ["mydriver"],
"connection_string": "mydb+mydriver://{username}:{password}@{host}:{port}/{database}",
"default_port": 1234,
"parameters": {
"username": "Database username",
"password": "Database password",
"host": "Database host",
"port": "Default 1234",
"database": "Database name",
},
}
Step 4: Implement Custom SQL Behavior
Override methods and attributes to handle dialect-specific SQL generation. Common customizations include:
Time-grain expressions for date truncation:
from superset.constants import TimeGrain
_time_grain_expressions = {
None: "{col}",
TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
TimeGrain.DAY: "DATE_TRUNC('day', {col})",
}
Column type mappings to convert native types to Superset's generic types:
import re
from superset.utils.core import GenericDataType
column_type_mappings = (
(re.compile(r"^mydb_int$", re.IGNORECASE),
None, # Use SQLAlchemy generic type
GenericDataType.NUMERIC),
(re.compile(r"^mydb_varchar$", re.IGNORECASE),
None,
GenericDataType.STRING),
)
Error handling by mapping regex patterns to SupersetError objects in the custom_errors dictionary.
Step 5: Verify Autodiscovery
No manual registration is required. The load_engine_specs function in superset/db_engine_specs/__init__.py automatically imports your module and the get_engine_spec function matches it using supports_backend.
When a user creates a database connection with a URL like mydb://..., Superset calls:
# From superset/db_engine_specs/__init__.py
def get_engine_spec(backend: str, driver: Optional[str] = None) -> type[BaseEngineSpec]:
engine_specs = load_engine_specs()
# Driver-aware lookup
if driver is not None:
for spec in engine_specs:
if spec.supports_backend(backend, driver):
return spec
# Backend-only fallback
for spec in engine_specs:
if spec.supports_backend(backend):
return spec
return BaseEngineSpec
Your class will be selected if engine == "mydb" or if the backend matches any of your engine_aliases.
Testing Your New Database Dialect
Create unit tests to verify your engine spec behaves correctly:
# tests/unit_tests/db_engine_specs/test_mydb.py
from superset.db_engine_specs.mydb import MyDBEngineSpec
def test_mydb_engine_name():
assert MyDBEngineSpec.engine_name == "MyDB"
assert MyDBEngineSpec.engine == "mydb"
assert MyDBEngineSpec.supports_backend("mydb")
assert MyDBEngineSpec.supports_backend("mydb_legacy") # alias test
def test_time_grain_expressions():
spec = MyDBEngineSpec
assert spec._time_grain_expressions is not None
Run the test suite with:
pytest tests/unit_tests/db_engine_specs/test_mydb.py -v
Before submitting, run pre-commit run --all-files to ensure the Apache license header is present and code formatting complies with the project standards.
Summary
- Create a module in
superset/db_engine_specs/(e.g.,mydb.py) containing a class that inherits fromBaseEngineSpec. - Define identification attributes including
engine,engine_name,engine_aliases, anddriversso Superset can match your dialect to connection URLs. - Add metadata using
DBEngineSpecMetadatato provide UI documentation, connection string templates, and parameter descriptions. - Implement custom behavior by overriding methods for time-grain expressions, column type mappings, error handling, and schema management.
- Rely on autodiscovery—Superset automatically loads your spec via
load_engine_specsand matches it usingsupports_backendwithout requiring manual registration. - Test your implementation with unit tests in
tests/unit_tests/db_engine_specs/and runpre-commitchecks before contributing.
Frequently Asked Questions
What is the minimum required configuration for a new engine spec?
At minimum, you must create a class inheriting from BaseEngineSpec and define the engine attribute (matching the SQLAlchemy dialect name) and engine_name (the display name). The supports_backend method in the base class uses these attributes to determine if your spec handles a given connection URL. Without these, Superset cannot discover or identify your dialect.
How does Superset automatically discover new database dialects?
Superset uses the load_engine_specs function in superset/db_engine_specs/__init__.py to iterate through all Python modules in the superset/db_engine_specs/ directory using pkgutil.iter_modules. It imports each module and collects any class that satisfies is_engine_spec (typically classes inheriting from BaseEngineSpec). When a user creates a database connection, get_engine_spec matches the URL against loaded specs using supports_backend.
Can I extend an existing engine spec instead of creating a new one?
Yes, you can inherit from existing engine specs rather than BaseEngineSpec. For example, if your database is PostgreSQL-compatible, you can inherit from PostgresEngineSpec in superset/db_engine_specs/postgres.py and only override the methods that differ. This approach reduces code duplication and ensures you inherit tested behavior for standard SQL operations while customizing only the dialect-specific features your database requires.
Where should I place tests for my custom engine spec?
Place unit tests in tests/unit_tests/db_engine_specs/ following the naming convention test_<dialect>.py. For integration tests that require a live database connection, use tests/integration_tests/db_engine_specs/. Reference existing test files like test_postgres.py for patterns on testing time-grain generation, column type mapping, and backend support detection. Run tests with pytest tests/unit_tests/db_engine_specs/test_mydb.py and ensure all new files include the Apache license header by running pre-commit run --all-files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →