Release notes¶
The canonical changelog lives in CHANGELOG.md at the repository root and is mirrored here. Format follows Keep a Changelog; versions follow Semantic Versioning.
Unreleased¶
No unreleased changes yet.
0.9.0 - 2026-05-17¶
Added¶
Read-only database LOAD support.
LOAD semantic_viewsnow succeeds on a read-only DuckDB database. Previously-defined semantic views can be queried vialist_semantic_views(),describe_semantic_view(), andFROM semantic_view(...)against a database opened withread_only=True(Python) or--readonly(CLI). On a read-only database that was never bootstrapped, the catalog is treated as empty rather than raising a missing-table error:list_semantic_views()returns zero rows, anddescribe_semantic_view('x')/FROM semantic_view('x', ...)return the standardsemantic view 'x' does not existerror for any name.Read-only DDL surfaces DuckDB’s standard error.
CREATE SEMANTIC VIEW,DROP SEMANTIC VIEW, andALTER SEMANTIC VIEWagainst a read-only database fail with DuckDB’s standardCannot execute statement of type "..." on database "..." which is attached in read-only mode!error rather than the previous confusing schema-create failure at LOAD time.examples/readonly_load.pydemonstrating the open-writable → bootstrap → close → reopen-readonly → query → catch-DDL-error workflow.just test-readonlyrecipe +test/integration/test_readonly_load.pyPython integration test (three scenarios: fresh read-only file, bootstrapped reopen, DDL rejection) andtest/sql/readonly_load.testwritable-side smoke fixture. Wired intojust test-all.
Fixed¶
Quoted identifier handling in
CREATE / DROP / ALTER / DESCRIBE / SHOW COLUMNS SEMANTIC VIEW. All five DDL forms now accept any combination of quoted, partially-quoted, and unquoted fully-qualified names — e.g.CREATE OR REPLACE SEMANTIC VIEW "memory"."main"."orders_sv" AS ...,CREATE SEMANTIC VIEW main."orders_sv" AS ..., andCREATE SEMANTIC VIEW orders_sv AS ...all store the same bare key, andFROM semantic_view('orders_sv', ...)resolves them uniformly.ALTER ... RENAME TOnormalises both the source name and the new-name target. Error messages reference the unquoted bare name. Previously, a quoted FQN was stored verbatim (quotes and all) as the lookup key, which made any subsequentsemantic_view('orders_sv', ...)call return “view does not exist”.Triple-quoted identifiers in expanded SQL. When a
TABLES (o AS "memory"."main"."orders" ...)clause used a quoted source-table reference, the expansion path re-quoted each part producing strings like"""memory"""."""main"""."""orders"""in the generatedFROMclause. The expansion now operates on parsed identifier parts and emits exactly one pair of quotes per part regardless of input shape, restoringEXPLAIN SEMANTIC VIEWlegibility and (in the rare case the source-table reference had embedded special chars) correctness of the generated SQL.
Known limitations¶
The v0.1.0 → v0.2.0 companion-file migration cannot run on a read-only database (the migration INSERTs into
semantic_layer._definitionswhich requires write access). Practical impact is near-zero — the companion-file format is four milestone versions stale and any database last opened with v0.2.0+ has already been migrated. If you have a v0.1.0-era database that has never been opened by any newer release, open it once writable to complete the migration before reverting to read-only.
0.8.0 - 2026-05-06¶
Added¶
Transactional DDL.
CREATE,DROP, andALTER SEMANTIC VIEWnow participate in the caller’s transaction.BEGIN ... ROLLBACKrolls back uncommitted catalog changes andBEGIN ... COMMITpersists them, matching the contract that ADBC, dbt, and other transaction-aware clients expect.parser_overrideextension hook. Recognised DDL is rewritten into nativeINSERT/UPDATE/DELETEagainstsemantic_layer._definitionsand executed on the caller’s connection. Non-matching statements fall through to DuckDB’s default parser unchanged.All four
CREATEforms transactional: inlineASkeyword body, inlineFROM YAML $$ ... $$,FROM YAML FILE '<path>'(includinghttps://and S3 paths via httpfs), andCREATE OR REPLACE/CREATE IF NOT EXISTSvariants.DROP / ALTER race guards. Non-
IF EXISTSDROP SEMANTIC VIEWandALTER SEMANTIC VIEW … RENAME / SET COMMENT / UNSET COMMENTnow emit a snapshot-consistent existence check on the caller’s connection before the DML. If a concurrent commit lands between the catalog pre-check (committed-state read on a separate connection) and the DML, the user seessemantic view '<name>' was concurrently droppedinstead of a silent no-op.IF EXISTSvariants keep their silent-no-op contract.CatalogReaderRAII.prepared_lookupandexecute_list_alluse internalPreparedStmtandQueryResultguards. Manualduckdb_destroy_*calls along error paths are gone.ParserOptionssize assert. A static assert pinssizeof(ParserOptions) == 32against DuckDB v1.5.2 (the upstream version pinned via the=1.10502.0duckdb-rs crate). Silent layout drift previously surfaced as garbage parser errors at position 0; future DuckDB bumps now fail fast at compile time.Actionable error when
allow_parser_override_extensionisDEFAULTorSTRICT(e.g. afterCALL disable_peg_parser()resets the setting). Issuing semantic DDL on such a connection now producesParser Error: semantic_views: parser_override is not active for this connection (allow_parser_override_extension is 'DEFAULT' or 'STRICT'). Re-enable with: SET allow_parser_override_extension='FALLBACK';with caret positioned at the start of the statement.ADBC end-to-end test (
test/integration/test_adbc_transactions.py, runnable viajust test-adbc) exercisingautocommit=Falserollback / commit semantics for inline, FROM YAML FILE, ALTER, and DROP forms — proves the original ADBC bug is fixed end-to-end.Concurrent-CREATE Python integration test (
test/integration/test_concurrent_ddl.py, runnable viajust test-concurrent).INSERT OR REPLACErow-count, byte-identical rollback (MD5), and same-txnlist_semantic_viewsvisibility cases inv080_transactional_ddl.test.Type-inference under
BEGIN/COMMITintest_type_inference.py.Arbitrary-bytes FFI fuzz target (
fuzz_parser_override_ffi).Caret-rendering sqllogictest fixtures pinning caret alignment across CREATE / DROP / ALTER / multi-line / UTF-8 / multi-DB / extension-reload paths.
peg_compat.testregression coverage that the override path keeps working under DuckDB’s experimental PEG parser, so v0.8.0’s transactional DDL survives the upcoming parser switch. Under PEG, every DDL form (includingDESCRIBEandSHOW) works because parser_override fires before whichever parser is active.
Changed¶
Architectural unification.
parser_overrideis the sole DDL entry point. Every recognised form —CREATE(all four variants),DROP,ALTER,DESCRIBE,SHOW SEMANTIC *,GET_DDL,READ_YAML_FROM_SEMANTIC_VIEW— is rewritten by a single Rust dispatch and re-parsed by DuckDB on the caller’s connection. The legacyparse_function/sv_ddl_internaltable-function fallback was retired (~1500 LOC net deletion). One execution path means transactional semantics, error reporting, and PEG/Bison compatibility are all uniform.CatalogStateHashMap removed. All catalog reads now query_definitionsdirectly through a single sharedCatalogReader. This eliminates the divergence risk between the HashMap and the on-disk table that the old write-through-both pattern carried.
Fixed¶
FFI UTF-8 hardening.
sv_parser_override_rustnow validates input bytes with checkedfrom_utf8instead offrom_utf8_unchecked. Malformed input cleanly defers to the default parser instead of triggering UB.parse_table_function_calltightening. The internal helper now rejectsfoo(,),foo('a',)(trailing comma), andfoo('a' 'b')(missing comma between args). Previously these silently parsed as zero-arg or merged-arg calls.Validation errors arrive as parse-time errors with caret rendering.
CREATE,DROP, andALTER SEMANTIC VIEWvalidation failures (e.g.semantic view 'X' does not exist, unknown clause) surface asParser Error: ... LINE 1: ... ^with the caret aligned to the offending token, matching DuckDB’s native parser-error rendering. Internally,parser_overridekeeps the success / transactional path (rewrite to native SQL, re-parse on caller’s connection); validation failures defer (DISPLAY_ORIGINAL_ERROR), the default parser fails on the unrecognised DDL prefix, and DuckDB callsparse_function, which re-runs validation and returnsDISPLAY_EXTENSION_ERRORwitherror_locationset to the offending byte offset.
Known limitations¶
semantic_view(...)queries do not see uncommitted writes to user tables in the same transaction. Expansion runs on a separatequery_conn, which only sees committed state. Workaround: commit the user-table writes before querying. Inline expansion will be revisited when DuckDB 2.0’s PEG grammar-extension API ships.A
CREATE SEMANTIC VIEWissued in the same uncommitted transaction is not visible to subsequent reads in that transaction (e.g.SHOW SEMANTIC VIEWSwill not list it until commit). With the HashMap gone, reads see only committed catalog state. Workaround: commit before reading. See TECH-DEBT item 19.CALL disable_peg_parser()resetsallow_parser_override_extensiontodefault, which silently bypasses parser_override hooks. Workaround: re-issueSET allow_parser_override_extension='FALLBACK'after disabling PEG. The extension installsFALLBACKon load, so a process that never enables PEG never sees this. See TECH-DEBT item 21.CREATE SEMANTIC VIEW IF NOT EXISTSis silent-no-op only against rows visible in the caller’s MVCC snapshot. Two parallel processes that each see the row absent will both attempt the INSERT and the loser seesConstraintException: Duplicate key "name: <view>" violates primary key constraintat commit — the same shape plainCREATEproduces under contention. Multi-process bootstrap scripts should catch this and treat it as success. See TECH-DEBT item 23.
0.7.2 - 2026-05-01¶
Fixed¶
Parser hook now strips leading SQL comments before matching
CREATE / ALTER / DROP / SHOW SEMANTIC VIEWDDL. Previously, any statement preceded by a/* ... */block comment or-- ... \nline comment was misclassified as not-our-statement and DuckDB surfacedParser Error: syntax error at or near "SEMANTIC". This made the extension unusable through dbt-duckdb (which unconditionally prepends a query annotation comment to every statement) and any other tool that prefixes annotations (sqlfluff, BI tools that prepend session/user metadata, etc.). Reported and diagnosed by an external user. Block comments are non-nesting, matching PostgreSQL/DuckDB semantics. Error-position byte offsets are preserved across the consumed comment span, so error carets continue to reference the original query string.
0.7.1 - 2026-04-26¶
Added¶
DDL-time type inference for dimensions and metrics:
data_type/DATA_TYPEcolumns in SHOW and DESCRIBE output now display inferred types (VARCHAR, BIGINT, DOUBLE, DATE, etc.) instead of empty stringsType inference runs automatically at
CREATE SEMANTIC VIEWtime on file-backed databases via a LIMIT 0 probe querySupported types: VARCHAR, BOOLEAN, integer types (TINYINT through UBIGINT), FLOAT, DOUBLE, DATE, TIME, TIMESTAMP (all variants), INTERVAL, UUID, BLOB, BIT; DECIMAL and parameterized types intentionally left empty to avoid lossy CAST
Derived metrics also receive inferred types when resolvable
In-memory databases continue to show empty
data_type(no persist connection available)
0.7.0 - 2026-04-24¶
Added¶
YAML definition format:
CREATE SEMANTIC VIEW name FROM YAML $$ ... $$as an alternative to SQL DDL keyword bodyYAML file loading:
CREATE SEMANTIC VIEW name FROM YAML FILE '/path/to/file.yaml'with DuckDBenable_external_accesssecurity enforcementDollar-quoting for inline YAML: both untagged (
$$...$$) and tagged ($yaml$...$yaml$) formsYAML export:
READ_YAML_FROM_SEMANTIC_VIEW('name')scalar function with lossless round-trip fidelityMaterialization declarations:
MATERIALIZATIONSclause in SQL DDL and YAML for declaring pre-aggregated tablesMaterialization routing engine: transparent query redirection to pre-aggregated tables on exact dimension/metric match
Semi-additive and window function metrics excluded from materialization routing (always expand from raw sources)
explain_semantic_view()now includes materialization routing decision (-- Materialization: <name>or-- Materialization: none)DESCRIBE SEMANTIC VIEWincludes MATERIALIZATION rows with table, dimensions, and metrics propertiesSHOW SEMANTIC MATERIALIZATIONS [IN view_name]command with LIKE/STARTS WITH/LIMIT filtering
0.6.0 - 2026-04-14¶
Added¶
Metadata annotations: COMMENT, SYNONYMS (aliases), PRIVATE/PUBLIC access modifiers on views, tables, dimensions, metrics, and facts
ALTER SEMANTIC VIEW SET COMMENT / UNSET COMMENT DDL for modifying view-level comments after creation
GET_DDL(‘SEMANTIC_VIEW’, ‘name’) scalar function for reconstructing re-executable CREATE OR REPLACE DDL from stored definitions
SHOW TERSE SEMANTIC VIEWS for reduced-column introspection output
SHOW COLUMNS IN SEMANTIC VIEW for a unified list of all dims, facts, and metrics with a
kindcolumnIN SCHEMA / IN DATABASE scope filtering for all SHOW SEMANTIC commands
Wildcard selection (
table_alias.*) in dimensions and metrics query parameters, expanding to all matching PUBLIC itemsQueryable FACTS via
facts := [...]parameter in the table function for row-level unaggregated resultsSemi-additive metrics via NON ADDITIVE BY (dimension [ASC|DESC] [NULLS FIRST|LAST]) for snapshot-style aggregation using CTE-based ROW_NUMBER
Window function metrics via PARTITION BY EXCLUDING for non-aggregated, partition-aware computation
Synonyms and comment columns in all SHOW SEMANTIC command output
Comment and access_modifier properties in DESCRIBE SEMANTIC VIEW output
Mutual exclusion: facts + metrics in same query produces a blocking error
Mutual exclusion: window function metrics + aggregate metrics in same query produces a blocking error
SHOW SEMANTIC DIMENSIONS FOR METRIC shows
required=TRUEfor window partition dimensions
Changed¶
FFI catch_unwind wrapping on all 25 entry points (Rust panics no longer unwind through C++ stack frames)
Graceful lock-poison handling across all catalog and query paths (error return instead of panic)
Cycle detection and MAX_DERIVATION_DEPTH=64 limit for derived metrics and facts
DimensionName/MetricName newtypes with case-insensitive semantics replace bare strings in query resolution
Resolution loop deduplication via generic resolve_names helper
0.5.5 - 2026-04-05¶
Added¶
Snowflake-aligned column schemas for all SHOW SEMANTIC commands (VIEWS, DIMENSIONS, METRICS, FACTS)
Snowflake-aligned DESCRIBE SEMANTIC VIEW property-per-row format
Metadata fields: created_on timestamp, database_name, schema_name on semantic view model
Per-fact output_type metadata
Changed¶
Refactored expand.rs into expand/ module directory (7 submodules)
Refactored graph.rs into graph/ module directory (5 submodules)
Extracted shared util.rs and errors.rs as leaf modules to break circular dependencies
0.5.4 - 2026-03-31¶
Added¶
UNIQUE constraints on tables in TABLES clause with automatic cardinality inference for relationships
Implicit PK reference resolution (REFERENCES target without column list resolves to target’s PRIMARY KEY)
ALTER SEMANTIC VIEW RENAME TO for renaming views
SHOW SEMANTIC DIMENSIONS / METRICS / FACTS introspection commands
LIKE, STARTS WITH, and LIMIT filtering for all SHOW SEMANTIC commands
Documentation site (Sphinx + Shibuya theme on GitHub Pages)
Community Extension Registry descriptor (description.yml)
MAINTAINER.md contributor documentation
Changed¶
DuckDB version support: 1.5.x (latest) + 1.4.x LTS with dual CI matrix
Relationship cardinality inferred from PK/UNIQUE constraints instead of explicit keywords
Removed¶
Explicit cardinality keywords on relationships (breaking: views must be recreated)
0.5.3 - 2026-03-15¶
Added¶
FACTS clause for named reusable row-level sub-expressions in semantic view definitions
Derived metrics (metric-on-metric composition with DAG resolution and cycle detection)
Fan trap detection with blocking errors for one-to-many aggregation fan-out
Role-playing dimensions (same table via multiple join paths)
USING RELATIONSHIPS clause for explicit join path selection in queries
Multi-level fact inlining with proper parenthesization for operator precedence
0.5.2 - 2026-03-13¶
Added¶
SQL keyword DDL body: TABLES, RELATIONSHIPS, DIMENSIONS, METRICS clauses replace function-call syntax
PK/FK relationship model with table aliases and graph-validated JOIN synthesis
Alias-based query expansion with qualified column names (direct FROM+JOIN instead of CTE flattening)
Parser robustness: token-based keyword matching tolerates arbitrary whitespace
Adversarial input hardening (null bytes, embedded semicolons, Unicode homoglyphs, control characters)
Removed¶
Function-call DDL body syntax (breaking:
define_semantic_view()interface retired)
0.5.1 - 2026-03-09¶
Added¶
DROP SEMANTIC VIEW and DROP SEMANTIC VIEW IF EXISTS
CREATE OR REPLACE SEMANTIC VIEW
CREATE SEMANTIC VIEW IF NOT EXISTS
DESCRIBE SEMANTIC VIEW
SHOW SEMANTIC VIEWS
Error location reporting with character positions (caret indicators in DuckDB output)
Clause-level error hints and “did you mean?” fuzzy suggestions for misspelled clause/view names
Parser property-based tests (proptests) for DDL parsing
0.5.0 - 2026-03-08¶
Added¶
Native
CREATE SEMANTIC VIEWDDL syntax via C++ parser extension hookParser fallback hook registration (C_STRUCT entry + C++ helper)
Rust FFI trampoline for detecting
CREATE SEMANTIC VIEWprefixStatement rewriting pipeline (native DDL to function-based execution)
Dedicated DDL connection to avoid lock conflicts
0.4.0 - 2026-03-03¶
Changed¶
Time truncation expressed via dimension
exprdirectly (e.g.,date_trunc('month', created_at))DDL simplified from 6 to 4 named parameters
Query function simplified from 3 to 2 named parameters
Removed¶
time_dimensionsDDL parameter (breaking)granularitiesquery parameter (breaking)
0.3.0 - 2026-03-03¶
Changed¶
Replaced binary-read dispatch with zero-copy vector references (
duckdb_vector_reference_vector)Streaming chunk-by-chunk output instead of collect-all-then-write
Type mismatches handled at SQL generation time via
build_execution_sqlcast wrapper
Removed¶
~600 LOC of per-type read/write dispatch code
0.2.0 - 2026-03-03¶
Added¶
C++ shim infrastructure for Rust+C++ boundary (vendored DuckDB amalgamation via cc crate)
Time dimensions with granularity coarsening and per-query granularity override
pragma_query_tcatalog persistence (replaced sidecar file with DuckDB-native table persistence)Scalar function DDL interface (
define_semantic_view())Snowflake-aligned STRUCT/LIST DDL syntax
EXPLAIN support for expanded SQL inspection
Typed output columns (zero-copy vector reference with runtime type validation)
DuckDB type-mapping with property-based tests
DuckLake integration test suite and CI
Removed¶
Sidecar file persistence (replaced by pragma_query_t)
0.1.0 - 2026-02-28¶
Added¶
Initial extension scaffold using
duckdb/extension-template-rsMulti-platform CI build matrix (Linux x86_64/arm64, macOS x86_64/arm64, Windows x86_64)
Scheduled DuckDB version monitor with automated PR creation
Code quality gates:
rustfmt,clippy(pedantic),cargo-deny, 80% coverageDeveloper task runner (
just) withjust setupone-command dev environmentPre-commit hooks via
cargo-husky(rustfmt + clippy)Semantic view definition storage and round-trip persistence across DuckDB restarts
Expansion engine: automatic GROUP BY and JOIN generation from dimension/metric declarations
Query interface via table function
semantic_view('view', dimensions := [...], metrics := [...])list_semantic_views()anddescribe_semantic_view()introspection functionsFuzz targets for FFI boundary testing