Execute Stage¶
The execute stage runs queries and retrieves data for compiled datafaces.
Entry Points¶
Executor¶
The main class for executing queries:
Executor
¶
Executor(face: CompiledFace, adapter_registry: Any | None = None, query_registry: dict[str, CompiledQuery] | None = None, project_root: Path | None = None, use_cache: bool = True, duckdb_cache: QueryResultCache | None = None)
Executes queries for datafaces.
Stage: EXECUTE (Service Module)
The executor manages query execution for a compiled dataface. It handles: - Query lookup from the face - Adapter selection based on query type - Result caching for efficiency - Variable substitution
Does NOT: - Compile datafaces (use compile module) - Render charts (use render module)
| ATTRIBUTE | DESCRIPTION |
|---|---|
face |
The compiled dataface
|
adapter_registry |
Registry of adapters (optional)
|
query_registry |
Complete query registry (for cross-references)
|
Example
from dataface.core.compile import compile from dataface.core.execute import Executor
result = compile(yaml_content) executor = Executor(result.face, query_registry=result.query_registry)
Execute a query¶
data = executor.execute_query("sales", {"year": 2024}) print(data) # [{"date": "2024-01", "amount": 1000}, ...]
Initialize executor.
| PARAMETER | DESCRIPTION |
|---|---|
face
|
Compiled dataface
TYPE:
|
adapter_registry
|
Optional adapter registry (uses default if not provided)
TYPE:
|
query_registry
|
Optional query registry for cross-file references
TYPE:
|
project_root
|
Root directory for resolving relative file paths
TYPE:
|
use_cache
|
Default cache behavior for query execution
TYPE:
|
duckdb_cache
|
Optional DuckDBCache for persistent caching (Suite context)
TYPE:
|
Source code in dataface/core/execute/executor.py
execute_query
¶
execute_query(query_name: str, variables: VariableValues | None = None, use_cache: bool | None = None, force_refresh: bool = False) -> list[dict[str, Any]]
Execute a query and return results.
Stage: EXECUTE (Main Entry Point)
Looks up the query by name, resolves the appropriate adapter, executes the query, and returns the data.
| PARAMETER | DESCRIPTION |
|---|---|
query_name
|
Name of query to execute (may include "queries." prefix)
TYPE:
|
variables
|
Variable values for query resolution
TYPE:
|
use_cache
|
Whether to use cached results if available
TYPE:
|
force_refresh
|
If True, clear both success and failure caches for this query and re-run it from scratch.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[dict[str, Any]]
|
List of dictionaries with query results (each dict is a row) |
| RAISES | DESCRIPTION |
|---|---|
ExecutionError
|
If query not found or execution fails |
CachedQueryFailure
|
If a cached failure exists within TTL |
Example
data = executor.execute_query("sales", {"year": 2024}) for row in data: ... print(f"{row['date']}: {row['amount']}")
Source code in dataface/core/execute/executor.py
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | |
execute_chart
¶
execute_chart(chart: CompiledChart | str, variables: VariableValues | None = None, use_cache: bool | None = None) -> list[dict[str, Any]]
Execute the query for a chart.
Convenience method that handles chart → query lookup.
| PARAMETER | DESCRIPTION |
|---|---|
chart
|
CompiledChart or chart name string
TYPE:
|
variables
|
Variable values
TYPE:
|
use_cache
|
Whether to use cache (defaults to instance setting)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[dict[str, Any]]
|
Query results for the chart |
Example
chart = face.charts["revenue"] data = executor.execute_chart(chart, {"year": 2024})
Source code in dataface/core/execute/executor.py
Adapters¶
Adapters connect to different data sources. Each query type has a corresponding adapter.
Base Adapter¶
BaseAdapter
¶
Bases: ABC
Base interface for query adapters.
All adapters must implement this interface to execute queries against different backends (MetricFlow, SQL, HTTP, CSV, etc.).
The unified interface pattern uses:
- supported_types: Property returning set of query types this adapter handles
- Type guards for type-safe field access in execute methods
Subclasses must implement
- supported_types: Property returning Set[str] of supported query types
- _execute(): Perform the actual query execution
Optional override
- _can_execute(): Override for custom eligibility logic beyond type matching
Example
class MyAdapter(BaseAdapter): ... @property ... def supported_types(self) -> Set[str]: ... return {"sql"} ... ... def _execute(self, query, variables): ... if is_sql_query(query): ... # Type checker knows query.sql exists ... return self._run_sql(query.sql)
supported_types
abstractmethod
property
¶
Query types this adapter can execute.
| RETURNS | DESCRIPTION |
|---|---|
set[str]
|
Set of query type strings (e.g., {"sql"}, {"csv", "http"}) |
Example
adapter.supported_types
can_execute
¶
Check if this adapter can execute the given query.
Uses supported_types for type-based routing. Override _can_execute() for additional custom eligibility logic.
| PARAMETER | DESCRIPTION |
|---|---|
query
|
CompiledQuery object (guaranteed by compiler)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
True if this adapter can execute the query, False otherwise |
Source code in dataface/core/execute/adapters/base.py
execute
¶
execute(query: CompiledQuery, variables: VariableValues | None = None, params: QueryParams = None) -> QueryResult
Execute a query and return results.
| PARAMETER | DESCRIPTION |
|---|---|
query
|
CompiledQuery object (guaranteed by compiler)
TYPE:
|
variables
|
Optional dictionary of variable values for query resolution
TYPE:
|
params
|
Optional pre-computed parameter values for parameterized execution. When provided, the adapter should use these params directly instead of re-processing variables. This is used by batch execution where parameterization happens before adapter execution.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryResult
|
QueryResult containing data or error information |
Source code in dataface/core/execute/adapters/base.py
SQL Adapter¶
For executing SQL queries against databases (DuckDB, PostgreSQL, etc.):
SqlAdapter
¶
SqlAdapter(dbt_project_path: str | None = None, use_example_db: bool = False, connection_string: str | None = None, project_root: Path | None = None, profile_type: str = 'duckdb', read_only: bool = True, duckdb_config: dict[str, Any] | None = None)
Bases: BaseAdapter
Adapter for executing raw SQL queries.
DuckDB sources use the raw duckdb driver (parameterized, fast). All other warehouses use build_adapter() from dbt_adapter_factory.
Supported query types: sql
Example
adapter = SqlAdapter() query = SqlQuery(sql="SELECT * FROM users WHERE id = {{ user_id }}") result = adapter.execute(query, {"user_id": 1})
Initialize SQL adapter.
| PARAMETER | DESCRIPTION |
|---|---|
dbt_project_path
|
Path to dbt project (default: current directory)
TYPE:
|
use_example_db
|
If True, use DuckDB example database instead of dbt config
TYPE:
|
connection_string
|
DuckDB connection string (e.g., ":memory:" or file path)
TYPE:
|
project_root
|
Root directory for resolving relative file paths in read_csv()
TYPE:
|
profile_type
|
Database type for dialect selection (e.g., 'postgres', 'duckdb')
TYPE:
|
read_only
|
If True (default), open DuckDB connections in read-only mode. File-based DuckDB: opened with read_only=True — the driver refuses all writes. In-memory DuckDB (:memory:): always opened read-write regardless of this flag because there is no other way to populate an in-memory database; this is a known exception. enable_external_access=False is still forced for :memory: when read_only=True. When read_only=True, enable_external_access=False is forced on the DuckDB config regardless of any user-supplied duckdb_config. This blocks httpfs URL fetches and read_csv/read_parquet/read_json against local and remote paths. To allow external access, pass read_only=False explicitly. Non-DuckDB warehouses (Postgres, Snowflake, BigQuery, etc.) are not affected by this flag — they have no native read-only driver knob. Use SELECT-only credentials/roles at the warehouse level for those. Callers that must write (cache backends, test fixture setup) should pass read_only=False explicitly.
TYPE:
|
duckdb_config
|
Optional DuckDB config dict for the default connection. When read_only=True, enable_external_access in this dict is overridden to False. To enable external access, use read_only=False.
TYPE:
|
Source code in dataface/core/execute/adapters/sql_adapter.py
list_sources
¶
Return the project's configured source profiles (lazy-loaded and cached).
Source code in dataface/core/execute/adapters/sql_adapter.py
get_source_config
¶
Look up source config for a profile name from project sources.
register_source
¶
Register a synthetic source against this adapter.
Used by surfaces that have a single warehouse connection but no
_sources.yaml (the dft inspect HTML server is the canonical
case). Idempotent on the source name: a project-source by the same
name (or a previous synthetic registration) always wins.
Source code in dataface/core/execute/adapters/sql_adapter.py
close
¶
Close all database connections.
Source code in dataface/core/execute/adapters/sql_adapter.py
CSV Adapter¶
For loading data from CSV files:
CsvAdapter
¶
Bases: BaseAdapter
Adapter for executing queries against CSV files.
Supported query types: csv
Loads data from CSV files and supports: - Column selection - Row filtering (exact match, AND logic) - Result limiting
Example
adapter = CsvAdapter() query = CsvQuery(file="data/sales.csv", columns=["date", "amount"]) result = adapter.execute(query)
Initialize CSV adapter.
| PARAMETER | DESCRIPTION |
|---|---|
project_root
|
Root directory of the project (for resolving relative paths)
TYPE:
|
Source code in dataface/core/execute/adapters/csv_adapter.py
HTTP Adapter¶
For fetching data from REST APIs:
HttpAdapter
¶
Bases: BaseAdapter
Adapter for executing HTTP/REST API queries.
Supported query types: http
Fetches data from REST API endpoints with support for: - Multiple HTTP methods (GET, POST, PUT, DELETE, PATCH) - Custom headers - Query parameters - Request body (JSON)
Example
adapter = HttpAdapter() query = HttpQuery( ... url="https://api.example.com/users", ... headers={"Authorization": "Bearer {{ token }}"} ... ) result = adapter.execute(query, {"token": "xyz"})
Initialize HTTP adapter.
| PARAMETER | DESCRIPTION |
|---|---|
timeout
|
Request timeout in seconds
TYPE:
|
Source code in dataface/core/execute/adapters/http_adapter.py
dbt Adapter¶
For integrating with dbt models:
DbtAdapter
¶
DbtAdapter(dbt_project_path: Path | None = None, profile_name: str | None = None, target_name: str | None = None)
Bases: BaseAdapter
Adapter for executing SQL queries via dbt's adapter system.
Supported query types: sql
Leverages dbt's adapter API to execute SQL queries with support for dbt-specific features like ref(), source(), etc.
This adapter requires dbt-adapters and a warehouse-specific dbt package (e.g. dbt-duckdb, dbt-postgres) — not dbt-core. A valid dbt project with profiles.yml configuration is also required.
Example
adapter = DbtAdapter(dbt_project_path=Path("./my_dbt_project")) query = SqlQuery(sql="SELECT * FROM {{ ref('customers') }}") result = adapter.execute(query)
Initialize dbt adapter.
| PARAMETER | DESCRIPTION |
|---|---|
dbt_project_path
|
Path to dbt project (default: current directory)
TYPE:
|
profile_name
|
dbt profile name (default: from dbt_project.yml)
TYPE:
|
target_name
|
dbt target name (default: DBT_TARGET env var, then 'dev')
TYPE:
|
Source code in dataface/core/execute/adapters/dbt_adapter.py
MetricFlow Adapter¶
For querying MetricFlow metrics:
MetricFlowAdapter
¶
Bases: BaseAdapter
Adapter for executing MetricFlow (dbt Semantic Layer) queries.
Supported query types: metricflow
Executes queries against dbt's Semantic Layer using MetricFlow. In a full implementation, this uses the MetricFlow Python API or CLI.
Example
adapter = MetricFlowAdapter() query = MetricFlowQuery( ... metrics=["revenue", "orders"], ... dimensions=["date", "region"] ... ) result = adapter.execute(query)
Initialize MetricFlow adapter.
| PARAMETER | DESCRIPTION |
|---|---|
config_path
|
Optional path to MetricFlow configuration
TYPE:
|
Source code in dataface/core/execute/adapters/metricflow_adapter.py
Adapter Registry¶
AdapterRegistry
¶
Registry for managing and selecting query adapters.
The registry uses type-based routing from the unified query interface.
Adapters declare their supported types via the supported_types property,
and the registry routes queries to the appropriate adapter.
Priority order: 1. First registered adapter that supports the query type wins 2. dbt adapter is registered first for SQL (with dbt features) 3. Fallback SQL adapter for plain SQL without dbt
Example
from dataface.core.execute.adapters import build_adapter_registry registry = build_adapter_registry(Path(".")) result = registry.execute(SqlQuery(sql="SELECT 1"))
Initialize adapter registry.
Use build_adapter_registry() to get a fully configured registry.
| PARAMETER | DESCRIPTION |
|---|---|
project_root
|
Root directory for resolving relative file paths.
TYPE:
|
Source code in dataface/core/execute/adapters/adapter_registry.py
project_root
property
¶
Return the registry's configured project root, if any.
supported_types
property
¶
Get all query types supported by registered adapters.
| RETURNS | DESCRIPTION |
|---|---|
set[str]
|
Set of query type strings |
register
¶
Register an adapter.
Adapters are indexed by their supported types for fast lookup.
| PARAMETER | DESCRIPTION |
|---|---|
adapter
|
Adapter instance to register
TYPE:
|
Source code in dataface/core/execute/adapters/adapter_registry.py
get_adapters_for_type
¶
Get all adapters that support a given query type.
| PARAMETER | DESCRIPTION |
|---|---|
query_type
|
Query type string (e.g., "sql", "csv")
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[BaseAdapter]
|
List of adapters supporting this type (in registration order) |
Source code in dataface/core/execute/adapters/adapter_registry.py
get_adapter
¶
Get an adapter that can execute the given query.
Uses type-based routing for fast lookup, then checks additional eligibility via can_execute().
| PARAMETER | DESCRIPTION |
|---|---|
query
|
CompiledQuery object
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
BaseAdapter | None
|
Adapter instance or None if no adapter can execute the query |
Source code in dataface/core/execute/adapters/adapter_registry.py
execute
¶
execute(query: CompiledQuery, variables: VariableValues | None = None, params: QueryParams = None) -> QueryResult
Execute a query using the appropriate adapter.
| PARAMETER | DESCRIPTION |
|---|---|
query
|
CompiledQuery object
TYPE:
|
variables
|
Variable values for query resolution
TYPE:
|
params
|
Optional pre-computed parameter values for parameterized execution. When provided, the adapter should use these params directly instead of re-processing variables. This is used by batch execution where parameterization happens before adapter execution.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
QueryResult
|
QueryResult with data or error |
Source code in dataface/core/execute/adapters/adapter_registry.py
list_sql_sources
¶
Return configured SQL sources from registered SQL adapters.
Source code in dataface/core/execute/adapters/adapter_registry.py
resolve_source_config
¶
Resolve a full source_config dict for use with InspectConnection.
Returns the warehouse-specific dict (type + credentials).
| PARAMETER | DESCRIPTION |
|---|---|
source
|
Named source profile. When None, falls back to the registry's default SqlAdapter — but only if it's a DuckDB adapter (the connection_string is :memory: or a .duckdb path). For non-DuckDB SqlAdapters, raises DatafaceError because reconstructing config from (connection_string, profile_type) drops credentials, host, port, keyfile_json, etc.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
source_config dict with at least a 'type' key. |
| RAISES | DESCRIPTION |
|---|---|
DatafaceError
|
DF-EXECUTE-NO-DEFAULT-SOURCE if source is None and the default adapter is non-DuckDB; DF-EXECUTE-SOURCE-NOT-FOUND if the named source is missing but others are configured; DF-EXECUTE-SOURCE-NOT-FOUND-EMPTY if no sources are configured. |
Source code in dataface/core/execute/adapters/adapter_registry.py
Errors¶
errors
¶
Execution error types.
Stage: EXECUTE Purpose: Define error types for query execution failures.
These errors are raised during: - Query execution (QueryError) - Adapter resolution/execution (AdapterError) - Connection failures (ConnectionError)
All errors inherit from ExecutionError → DatafaceError for easy catching.
ExecutionError
¶
Bases: DatafaceError
Base error for all execution failures.
This is the parent class for all execution-related errors. Catch this to handle any execution error.
| ATTRIBUTE | DESCRIPTION |
|---|---|
message |
Human-readable error description
|
query_name |
Name of query that failed (if applicable)
TYPE:
|
Source code in dataface/core/execute/errors.py
QueryError
¶
Bases: ExecutionError
Error during query execution.
Raised when: - SQL syntax is invalid - Table/column doesn't exist - Query returns unexpected results
Example
try: ... executor.execute_query("broken_query") ... except QueryError as e: ... print(f"Query failed: {e}")
Source code in dataface/core/execute/errors.py
AdapterError
¶
Bases: ExecutionError
Error with adapter resolution or execution.
Raised when: - Adapter not found for query type - Adapter initialization fails - Adapter-specific error occurs
Example
try: ... executor.execute_query("unknown_type_query") ... except AdapterError as e: ... print(f"Adapter error: {e}")