Repository navigation
Changelog
All notable changes to this project will be documented in this file.
[0.7.0] - 2026-10-16
馃殌 Features
- Codegen-dto emits no backend alias; drop Dialect.metric_result_column_name (53-05)
- Databricks native decimals and bound filter values (driver >= 0.2.0)
- (codegen) Implement D9 on both --check commands
- Public Query, exported builder types, all in every module
- SemolinaError base for every Semolina error (D5)
- [breaking] CLI dependencies move to a [cli] extra; import semolina stays light (D6)
- SQLDialect is the SQL-generation ABC; to_sql() follows .using() (D7)
馃悰 Bug Fixes
- Alias every selected column to its field name on Snowflake and Databricks (53-02)
- DuckDB projects field names and filters before aggregation; refuse unselected order_by dimensions (53-03)
- Say DuckDB has no query for an OR across a dimension and a metric (53-03)
- Order a selected metric by its alias on Databricks (53-04)
- Complete a short Snowflake view name from the session (53-04)
- Pin the session database on the Snowflake record pool (53-04)
- (54-01) == None is IS NULL, other None comparisons raise, .where() checks its arguments
- (54-02) Escape LIKE wildcards in startswith, endswith and iexact
- (54-04) To_sql() writes each value as the dialect's SQL literal
- (54-05) Escape a pre-quoted view segment, refuse a dot inside one
- (54-05) DuckDB introspect describes the schema-qualified view, quoted
- (54-05) Snowflake introspect quotes each part of the view name
- (54-05) Mark value slots where the compiler emits them
- (54-05) Translate every ADBC introspection error, by type first
- (54-03) Compile a metric filter to HAVING on the wrapped metric
- (databricks) Quote the view name in introspect (FILT-05)
- Render time, UUID and numpy filter values as SQL literals
- Prefix a generated class name that is a Python keyword
- Order by the alias when a select alias shadows the metric's measure
- Report a DuckDB view in a foreign catalog as not found
- Name an unbalanced view-name quote, catch it in codegen, keep Snowflake preview instants
- (cli) Report a malformed view name under --check as an error line
- Keep non-ASCII letters in class names; check between() bounds by identity
- Render the values DuckDB used to bind into where_clause; refuse numpy datetimes
- (codegen) The DTO reader keeps a described field and skips ClassVar and private names
- Order a shadowed column by its own alias on every dialect
- Four edge cases from review (DTO hint, DuckDB case, ZoneInfo time, Field(...))
- Name a DTO view entry the way semolina codegen names the view
- Read a doubled quote in a quoted view segment as the SQL escape
- Quote the session database and schema introspect fills in on Snowflake
- Read only a Field(...) call as leaving a DTO field bound
- Report a view name codegen-dto cannot quote as an error line
- Keep Snowflake's db..view default-schema shorthand unquoted
- Name stray text outside a quoted view segment instead of a dot
- Render_inline with no params returns the SQL unchanged; scope the quoting note per dialect
- Read Field under an import alias, and a Field call inside Annotated
- Match the DTO provenance docstring after a formatter reflows it
- (codegen) Give non-identifier columns valid field names
- (codegen) Treat type as a soft keyword on every Python version
- (codegen) Settle field-name collisions ignoring case and reserve module names
- (codegen) Refuse unusable class names and never emit unparseable source
- (cli) Redact config errors; one exception-to-exit table for both commands
- (codegen) Byte-identical stdout and --output, atomic writes, isolated ruff
- (codegen) Expand ~ in DTO config, announce env override, honest --check
- (codegen) Refuse import-shadowing class names; exit 6 only on a failed probe
- (codegen) Derive class names clear of module imports; redact, exit 2, in-place fallback
- (codegen) Exit 1 on any temporary-file error; write fd paths in place
- (codegen) Databricks driver hint names dbc install; DUCKDB_DATABASE note only for duckdb
- (codegen) Write only descriptor paths in place; document ruff defaults
- (codegen) Read dotted file stems; keep warnings of DTO classes with no readable field
- (codegen) Generic[T] and star-imported chain-ending bases are skipped without a warning
- (codegen) NUL bytes on 3.11 and full-width keyword class names are clean errors
- (codegen) Resolve class-body names in the module; a non-field rebinding removes an inherited field
- (codegen) Follow re-exports to semolina; star-import exemption reads the base's file; skip method bodies
- (codegen) A subclass named like its imported base extends the import, not itself
- (codegen) Base-file warnings name the base's module; share compared_annotation; note namespace packages
- (codegen) Follow re-exports in dotted annotations; skip methods in the model reader; Annotated[()] compares as text
- (codegen) --check matches view names by the engine's identifier rules
- (codegen) Fold quoted view names only on dialects that ignore their case
- (query) Type the builder's parameters
- (registry) Lock registry mutations
- (packaging) Keep the doctest conftest in the package, out of the wheel
- Introspect() without [cli]; refuse limit(bool); hint on a broken CLI install
- Semolina.config stays reachable; CLI import bugs keep their traceback; lookups race-safe
- Import duckdb lazily in the test install helper
- A DuckDB fact query groups like Snowflake and Databricks (COR-1)
- A view the warehouse cannot describe is not a connection failure (COR-2)
- A connection that cannot be opened raises SemolinaConnectionError (ARCH-1)
- Introspect()'s result types live in semolina.introspection (ARCH-8)
- A DBAPI fetch after the result is drained returns an empty result
- Codegen imports a --backend module from the working directory, and says "could not connect" once
- Every error class lives in semolina.exceptions (ARCH-7)
- Query reports what it selects through public read-only properties (ARCH-3)
- Dialects name their backend, one backend table, to_sql takes a dialect (ARCH-5)
- Name the extension when DuckDB cannot load it; export the two new errors
- Require adbc-poolhouse 1.6.4, which stops async pools stalling past capacity
馃捈 Other
- (ruff) Exclude .planning, as prek already does
- (deps) Add griffelib for reading committed files in --check
馃殰 Refactor
- Delete the unused WHERE helpers that bypassed the HAVING split
- (cli) Remove dead cli/utils.py and its tests
- ARCH-4 shares engine state on a base and lifts introspection out of the methods
- ARCH-7 imports the engine errors from semolina.exceptions
- Share the cursors' non-I/O half through a private base (ARCH-2)
- Snowflake and Databricks share one introspection error translation; engines exports SemolinaIntrospectionError
馃摎 Documentation
- (planning) Propose D2, portable result column names (53-01)
- (planning) Record D2 review answers and the Snowflake/DuckDB comparison
- (planning) D2 decided as option A; sharpen the where_clause binding gap
- (planning) 53-01 complete; D2 decided
- (planning) 53-02 complete; 53-04 lists the integration tests to update
- DuckDB filter routing, the where_clause binding, and the order_by refusal (53-03)
- (planning) 53-03 complete; 53-05 also sweeps the warehouse SQL previews
- (planning) 53-04 live results; regenerate the type-fidelity artifact
- (planning) D4 live probe for metric filters on both warehouses (53-04)
- (planning) D4 decided by live measurement: metric filters compile to HAVING (53-04)
- (planning) Close 53-04; ALIAS-01 met; jaffle-shop live run moves to 54-03
- Show aliased warehouse SQL and field-name row keys (53-05)
- (planning) Close 53-05; ALIAS-02..04 met; ALIAS-05 waits on 54-03
- (54-02) Use a field the guide's model has in the escaping example
- (54-03) Metric filters are HAVING; the tutorial's OR example runs
- (planning) Record Phase 54 progress and the recording session it still needs
- Show the aliased SQL in the SQLBuilder docstring examples
- Fix the operator-precedence example that now raises
- [breaking] Say that a quoted view-name segment is written unescaped
- Describe NULL comparisons and literal LIKE lookups in the Field docstrings
- Fix the Snowflake render_literal backslash example and merge in_()'s Raises
- Note that Snowflake and Databricks reject a metric filter in WHERE for now
- --check no longer drifts across backends for column names alone
- Say which values to_sql() can't preview, and place the OR/NOT mix note
- Update pre-53 alias docstrings; warn off QUOTED_IDENTIFIERS_IGNORE_CASE
- DuckDBSQLBuilder overrides _build_template, not build_select_with_params
- Say where render_literal is used; drop jaffle-shop's removed Snowflake tests
- Codegen-dto fields are bare and portable; DuckDB inlines a dimension filter
- Databricks decimals are Decimal in the tutorials; old-driver symptom; untested bound types
- (codegen) Describe how codegen names awkward columns
- (codegen) Note case-insensitive collisions and reserved module names
- (planning) Start Phase 55; reword GEN-01 after the codegen naming rule
- (planning) 55-01 done
- (planning) 55-02 done
- (planning) 55-03 done
- (codegen) Document --output, --check reading it, and the new exit 6
- (planning) 55-04 done; Phase 55 summary for 55-01 to 55-04
- (planning) Bring D9 to its checkpoint with measured prototypes
- (codegen) List codegen-dto's exit 1 and 2 cases; "committed file" wording
- (planning) Phase 55 summary notes the PR #56 review fixes
- (codegen) Class naming, missing --check file exits 2, in-place write fallback
- (planning) Phase 55 summary notes the PR #56 round-2 fixes
- (codegen) What --check reads since D9, and what it accepts
- (planning) D9 decided (griffe) and 55-06 done; Phase 55 complete
- (codegen) Note that --check reads fields bound to an existing field object as non-fields
- (planning) Add Phase 58, a Fable code review and doc-writer docs pass before the v0.7.0 tag
- (planning) Record D5, D6 and D7 (56-01)
- (planning) D7 covers .using() only
- (query) Resolvable refs now that Query is documented
- State the sync/async naming rule and guard it (API-05)
- To_sql() follows .using(); name the not-registered error (56-06)
- (planning) Phase 56 done
- A changelog page with the 0.7.0 notes for 0.6 users (API-11)
- (planning) Phase 57 plans 01-04 done
- Changelog warns that every filter now refuses None
- (58) Pre-release review findings (REV-01)
- Docstrings and pages that described a different design (ARCH-3, 4, 6, 9, COR-3)
- (planning) Phase 58 progress, REV-01 done
- Check every page changed since 0.6.1 against the code (REV-03, research and author)
- Codegen docstrings name SemolinaGeneratedSourceError (ARCH-7)
- Apply the doc-writer editor and user-tester findings (REV-03)
- Read a query back through its properties; changelog for ARCH-3 and ARCH-7
- (planning) Phase 58 done; 0.7.0 waits on adbc-poolhouse 1.6.4
- (planning) The poolhouse hold is lifted; 57-05 waits on PR #63
馃帹 Styling
- (databricks) Sort the introspect imports
- Ruff format the new literal tests
- Ruff format
馃И Testing
- Expect every Snowflake/Databricks column aliased to its field name (53-02, red)
- Expect DuckDB's aliased projection, where_clause routing and order_by refusal (53-03, red)
- Expect Databricks to order a selected metric by its alias (53-04, red)
- Re-record Snowflake and Databricks cassettes with aliased columns (53-04)
- Qualify a bare Snowflake view name from the session (53-04, red)
- Expect the Snowflake record pool to pin the session database (53-04, red)
- Record Snowflake introspection of a bare view name (53-04)
- A plain-field DTO converts on Snowflake and Databricks (53-05, ALIAS-02)
- Expect codegen-dto to emit no backend alias and metric_result_column_name gone (53-05, red)
- DuckDB-backed codegen tests expect bare DTO fields (53-05)
- Read the tracer's nullability claim off the parsed annotations (53-05)
- (54-01) None in a filter, and .where() refusing a non-predicate
- (54-02) Lookup values match their wildcards literally
- (54-04) To_sql() renders each dialect's literals
- (54-05) Pre-quoted view segments are escaped; a dot inside one raises
- (54-05) DuckDB introspect honours the schema and quotes the name
- (54-05) Snowflake introspect quotes each part of the view name
- (54-05) A ? in a view or column name is not a placeholder
- (54-05) Every ADBC error is translated, by type before message
- (54-03) A metric filter compiles to HAVING on the wrapped metric
- (databricks) Introspect must quote the view name (FILT-05)
- (integration) HAVING, boolean, NULL and NULLS LAST checks awaiting recording (54-03)
- (integration) Record HAVING, boolean, NULL and NULLS LAST cassettes (54-03)
- (jaffle-shop) Delete the live warehouse test now its checks are recorded (54-03)
- DuckDB where_clause rejects time, UUID and numpy filter values (red)
- A view named none/true/false generates an invalid class name (red)
- ORDER BY a metric whose measure name is a select alias (red)
- DuckDB introspect of a mistyped catalog reads as a connection failure (red)
- Unbalanced-quote message, CLI ValueError and Snowflake aware-datetime preview (red)
- --check on a malformed view name exits 1 with an error line
- Non-ASCII view names keep their letters; between() checks None by identity
- Values DuckDB used to bind still reach where_clause; numpy datetimes refuse
- The DTO reader keeps a described field and skips ClassVar and private names
- A field whose column another select alias shadows orders by its own alias
- Call the standard rendering directly now DuckDB writes TIMESTAMPTZ
- DTO underscore hint names the real column; DuckDB shadows ignore case; ZoneInfo times and Field(...) fields
- DTO codegen names a view the way semolina codegen does (red)
- A pre-quoted view segment escaped the SQL way is not escaped twice (red)
- Introspect quotes the session database and schema it fills in (red)
- A default built by a call other than Field is not read as a DTO field (red)
- Codegen-dto reports a view name it cannot quote without a traceback (red)
- Snowflake introspect keeps the db..view default-schema shorthand (red)
- Text outside a quoted view segment is named as such, not as a dot (red)
- Render_inline with no params leaves a question mark alone (red)
- DTO reader follows Field import aliases and Field inside Annotated (red)
- A reformatted provenance docstring still marks a generated DTO (red)
- Wrap the reformatted-docstring test under the line limit
- Expect native Databricks decimals and bound filter values
- (integration) Record startswith/endswith LIKE ESCAPE filters live
- (integration) Record HAVING on a quoted lower-case Snowflake measure
- Name East as well as West in the endswith wildcard docstring
- East does not end in est, so only West leaks without the escape
- (integration) Re-record Databricks filter cassettes with bound params
- (integration) Record Databricks DECIMAL, DATE and bound-parameter checks
- Cite FILT-07 for the ? in a column name test
- (codegen) Reproduce non-identifier column names in generated models
- (codegen) Reproduce case-only collisions and import-shadowing field names
- (codegen) Failing tests for the validity gate and a loud ruff failure
- (cli) Failing tests for credential redaction and one exit-code table
- (codegen) Failing tests for output plumbing
- (codegen) Failing tests for config paths, env override note, check accuracy
- (codegen) Turn the --check accuracy corpus into tests
- (codegen) Failing tests for PR #56 review findings
- (codegen) Failing tests for PR #56 second-round review findings
- (codegen) Make the FIFO --output test unable to hang
- (codegen) Failing tests for temporary-file errors and fd paths in --output
- (codegen) Databricks driver hint and DUCKDB_DATABASE note for other backends (red)
- (codegen) Name probed drift when another view's failed probe exits 6 (red)
- (codegen) Only descriptor paths under /dev and /proc are written in place (red)
- (codegen) Failing tests for D9 behaviour beyond the corpus
- (codegen) Dotted file stems and fieldless DTO classes keep their warnings (red)
- (codegen) Rename tie-breaks; Generic[T] and star-imported bases end the chain (red)
- (codegen) A parser ValueError and full-width keyword names are refused (red)
- (codegen) Class-body names resolve in the module; removed inherited fields stay removed (red)
- (codegen) Base-file star imports, re-exported field classes, method locals (red)
- (codegen) A subclass named like its imported base; DTO bases in other modules (red)
- (codegen) Relative same-name base; base-file warnings name the base's module (red)
- (codegen) Failing tests for Annotated[()], dotted re-exports and field calls in methods
- (codegen) Failing tests for view-name matching by each engine's identifier rules
- (codegen) --check on Snowflake checks each spelling of a view against its own class
- (codegen) Failing test for view-name folding on a dialect with case-sensitive quoting
- (query) Failing basedpyright tests for builder misuse
- Failing tests for the public names and all in every module
- Mark deliberate builder misuse for the type checker
- Failing tests for a SemolinaError base (D5)
- Failing tests for the [cli] extra and a light import semolina (D6)
- Failing tests for SQLDialect and to_sql() following .using() (D7)
- Failing concurrency tests for the registry
- Failing tests for introspect without [cli], limit(bool), a broken CLI install
- Failing tests for semolina.config access, an import bug in the CLI, and a lookup racing unregister
- Failing check that copied cassettes say so (TEST-02)
- Copied cassettes and their tests say copied in their names (TEST-02)
- Failing tests for a retried semantic_views install (TEST-04)
- Retry the extension install once; run timing tests outside -n auto (TEST-04)
- The suite collects without duckdb installed (red)
- A fact query keeps duplicate rows on DuckDB (red, COR-1)
- A view that does not bind is reported as a connection failure (red, COR-2)
- Connection failures outside introspect() surface as driver errors (red, ARCH-1)
- Introspect() returns types defined in the codegen package (red, ARCH-8)
- Sync and async cursors guard and claim each read identically (ARCH-2)
- Codegen --check on an unparseable models file (TST-1)
- Create_async_engine by connection name and without anyio (TST-2)
- Run each DuckDB where_clause typed literal against the extension (TST-3)
- Pin the precedence of the exported resolve_column_key (TST-4)
- CLI output, write and config error paths (TST-5)
- Typer Exit/Abort and unlisted setup errors in the exit table (TST-6)
- Static reader robustness on cycles, bad MROs and odd constants (TST-7)
- DUCKDB_DATABASE naming the committed path prints no note (TST-8)
- A DBAPI fetch after the result is drained raises (red)
- A connection failure says "could not connect" twice in the CLI (red)
- A --backend module in the working directory cannot be imported (red)
- Every error lives in semolina.exceptions, duplicate columns raise a SemolinaError (red, ARCH-7)
- Query has no public way to read what it selects (red, ARCH-3)
- A dialect cannot name its backend, and to_sql() refuses a dialect object (red, ARCH-5)
- A DuckDB extension-load failure reads as a connection failure, two errors are not exported (red)
- More concurrent aexecute() calls than pool slots stall until the checkout timeout (red)
- Snowflake and Databricks keep separate copies of one error mapping; engines lacks SemolinaIntrospectionError (red)
鈿欙笍 Miscellaneous Tasks
- Auto-merge Dependabot PRs once every check passes
- Open and merge bot PRs as the GitHub App
- (duckdb-check) Find the bump target from PyPI and the published binaries
- (duckdb-check) Defer the bump on probe errors, label the manifest version
- Bump duckdb pin 1.5.5 -> 1.5.6
- Remove dead type-ignores; fail on unnecessary ones (API-09)
- Restore uv.lock revision 5; SemolinaError docs name the classes Semolina defines
- Type-check the jaffle-shop example; start CI on the duckdb-bump PR (TEST-03, TEST-05)
- Review nits on the phase 57 changes
- Review round 2 nits, held for a decision
- (release) 0.7.0