# Changelog
## 0.6.21 — 2026-08-25
- **Observation args for `rhai:` vistas.** A query-sourced table's script can
now read values supplied at open time through an `args` map
(`if "lob" in args { … }`), so a caller filters *before* aggregation instead
of wrapping a finished aggregate in an outer `WHERE`. Values the script
embeds bind as query parameters like any other scalar. The script can also
route between source tables on argument presence.
- **PostgreSQL vista parity with SQLite.** `PostgresTableShell` gained
`clone_shell`, `add_search`, `clear_search`, `set_page_size`, `fetch_page`
and `fetch_next`, and now advertises `can_search`, `can_set_page_size`,
`can_fetch_page` and `can_fetch_next`. Postgres tables previously
under-advertised, so consumers fell back to the slowest honest path: no
server-side quicksearch, and no declared page size. Postgres quicksearch
also matches case-insensitively (`ILIKE`), as SQLite's `LIKE` already did.
- **Identifiers escape embedded quotes.** `Identifier` renders `a"b` as
`"a""b"` rather than interpolating it raw. Identifiers were code-defined by
construction until observation args made them reachable from runtime values
(`ident(args.col)`).
- `from_as(<select>, alias)` in the Rhai vocabulary — derived tables, for
rank-then-regroup and top-N-plus-Other shapes.
- **Paged Postgres reads are a stable window.** `fetch_page`, `fetch_next` and
`fetch_window` order by the id column when the caller has set no order of
its own. `LIMIT`/`OFFSET` without `ORDER BY` lets PostgreSQL return rows in
any order — a parallel or bitmap scan readily does — so successive pages
could repeat one row and skip another with nothing to signal it.
- **Bug fix (data loss):** a Postgres cursor scan ended early on a table whose
id column is not unique. Rows are keyed by id, so duplicates collapse and a
full SQL page can arrive under-length; the scan read that as the end of the
set and dropped every remaining page. Exhaustion is now an EMPTY page, at
the cost of one extra round trip per scan. The source also warns when the
collapse happens, since it silently shrinks any read.
## 0.6.20 — 2026-08-22
- **Bug fix (data loss):** `delete_all` on a conditioned table deleted the whole
table. The SQLite, Postgres and MySQL sources built `DELETE FROM <table>` from
the table name alone and never read the table's conditions, so a caller that
narrowed a table and then deleted it lost every row. The conditions are now
applied, and an unconditioned table still truncates as before.
## 0.6.19 — 2026-08-16
- The SQLite, Postgres and MySQL shells implement `preview_query`, returning the
SELECT as it stands with values rendered inline. The executed form binds those
same values as parameters.
## 0.6.18 — 2026-08-12
- **Bug fix:** the Postgres, MySQL and SQLite shells resolve relations whose
target constructor returns a typed table. `get_ref_target` went through an
entity-typed downcast that failed, so a reference field's list of eligible
rows came back as an error.
## 0.6.17 — 2026-08-07
- **Bug fix:** a search keeps the conditions of the table. The search
looks in each column, and `AND` binds more tightly than `OR`. Thus
`role = 'admin' AND <first branch> OR <second branch>` let each row
that matched a later branch ignore the role condition. The branches
are now one group with brackets.
- `or_` and `and_` are also methods on the operation trait:
`price.gt(100).or_(featured.eq(true))`. The functions stay available.
- **Behaviour change:** `or_` and `and_` give a `ConditionGroup`, which
writes one set of brackets around the chain and no brackets around
each operand. `or_(a, b)` gave `(a) OR (b)`, and it now gives
`(a OR b)`. A chain stays flat: `a.or_(b).or_(c)` gives
`(a OR b OR c)`, not a nest. To make a different group, nest the
calls: `a.or_(b.or_(c))`.
## 0.6.16 — 2026-07-26
- **Behaviour change:** a Postgres vista no longer advertises `can_subscribe`
just because it is writable. `LISTEN` on a channel no trigger ever feeds
succeeds and then blocks forever, so the old flag promised a feed that may
never arrive — indistinguishable, to a consumer, from a table where nothing
happens. Watchability is now opt-in via
`PostgresVistaFactory::with_notify(true)`, by which the application declares it
installed the `{table}_changed` trigger. Postgres cannot be asked whether that
trigger exists, so the application is the only party that can answer honestly.
Callers relying on the implicit flag add one builder call; nothing else changes.
- Reference traversal carries that declaration across. `get_ref`,
`get_ref_target` and contained-reference traversal each build a fresh factory
internally, so without this a relation target came back unwatchable even on a
database whose tables all have triggers.
## 0.6.15 — 2026-07-23
- `add_op_condition` pushes non-equality filters into the SQL query for all three
flavours (SQLite/Postgres/MySQL): `!=`, `<`, `<=`, `>`, `>=`, and `in` /
`not in` (via `in_list` / `not_in_list` over a `Value::Array` operand). The
factories advertise `can_filter_operators`.
## 0.6.14 — 2026-07-23
- SQLite binds lower `Tag(8)` record references (`["table", id]` or
`"table:id"`) to their id — a numeric id binds as INTEGER, so
reference values from dropdowns insert cleanly into integer FK
columns instead of panicking `bind_by_cbor: unexpected CBOR value`.
- Binding a value SQLite can't express is now a typed error naming the
parameter position and value shape — never a panic (these values come
straight from user forms).
- Failed SQLite queries carry context: the SQL (truncated at 500 chars)
and a parameter-type summary (`?1=Text ?2=Integer …`, types only) —
enough to see which table/column a `datatype mismatch` points at.
## 0.6.13 — 2026-07-22
- Vista factories (SQLite/Postgres/MySQL) lower **dotted spec columns**
(`author.name`) as implicit-reference imports through
`with_active_columns`, after the spec's references are registered. A
dotted column previously went through the plain `add_column` path,
selecting a nonexistent identifier and reading empty. Declared-column
YAML now matches the typed API: dotted names traverse the has-one
chain and project as read-only correlated imports.
## 0.6.12 — 2026-07-21
- Implicit-references review fixes: vista factories flag computed columns
(implicit-reference imports, expression, lazy) `calculated` in metadata.
Quicksearch skips imported columns — the dotted identifier is not a real
field in a WHERE clause (SQLite would degrade it to a string literal and
match wrong rows; Postgres/MySQL would error at fetch time).
## 0.6.11 — 2026-07-21
- SQLite / Postgres / MySQL advertise `TableSource::supports_traversal`, so
implicit references (`Table::with_active_columns`) lower into nested
correlated scalar subqueries on their existing `related_correlated_condition`
— no native-path override needed. The vista factories advertise
`can_traverse_in_columns`.
## 0.6.10 — 2026-07-16
- CBOR↔JSON bridge is the shared `vantage-types` walker under a local `SqlDialect`;
behavior unchanged (NaN/Infinity as strings, UTF-8-else-hex bytes, `Tag(10)` decimals
as numbers). The `From<JsonValue>` conversions in all three drivers no longer carry an
`.expect()` panic path.
## 0.6.9 — 2026-07-16
- Postgres live subscriptions. The Postgres Vista advertises `can_subscribe` and
implements `watch_vista` via `PgListener`, streaming a coarse
`VistaChange::Invalidated` on every `LISTEN/NOTIFY` on the `{table}_changed`
channel (which the application feeds from a trigger). This lets `dio.watch()`
drive a Postgres-backed reactive view with no hand-written listener — the same
call the SurrealDB path uses.
## 0.6.8 — 2026-07-15
- SQLite vista factory honours the column spec's `lazy: <rhai script>` form —
the script sees the record built so far as `row`, chaining in declaration
order (requires the `rhai` feature, which now also enables
`vantage-vista/rhai`).
## 0.6.7 — 2026-07-02
- SQLite's shell is now `Clone` and implements `TableShell::clone_shell`, so a
consumer (e.g. a diorama paged scenery) can build a per-view ordered copy —
`clone_shell()` → `add_order` → `fetch_window` — to page rows in server order.
The clone is cheap: it copies the wrapped table's query state (the same clone
`fetch_window` already does per call) while the connection pool stays shared.
## 0.6.6 — 2026-06-28
- TLS support for the Postgres and MySQL connection pools: sqlx is now built with the `tls-rustls`
feature (rustls + ring + webpki), so a `DATABASE_URL` with `sslmode=require` negotiates an
encrypted connection. This is required against servers that enforce SSL — e.g. Amazon RDS for
PostgreSQL 15+, whose default parameter group sets `rds.force_ssl = 1` and rejects unencrypted
sessions. No API change; existing non-SSL connections keep working (sqlx defaults to
`sslmode=prefer`).
## 0.6.5 — 2026-06-28
- The Postgres table source honors `Table::with_text_id()` (vantage-table 0.6.9): a text-keyed
table binds its id as text on every by-id insert/get/replace/patch/delete, instead of coercing an
all-digit id to `bigint`. Tables without the flag keep the integer-coercing default.
- The Postgres Vista shell implements `fetch_window(offset, limit)` (and advertises
`can_fetch_window`), so windowed pagination works through the Vista facade, matching the SQLite
shell.
## 0.6.4 — 2026-06-26
- A NULL id read back from a row is no longer coerced to the literal string `"Null"`: rows with a
NULL id are skipped on read, and an insert whose `RETURNING` id comes back NULL (a `PRIMARY KEY`
with no `DEFAULT`/sequence and no supplied id) fails with an explanatory error instead of a bogus
id. Pair such tables with `Table::with_generated_id` to mint the id client-side. Applies to the
SQLite, Postgres, and MySQL table sources.
- On the explicit-id insert/replace paths the id the caller passes is now applied after the record,
so it stays authoritative even when a `before_insert` hook (e.g. an id generator) also wrote an id
into the record.
## 0.6.3 — 2026-06-25
- `AnySqliteType` implements `InvariantValue` (via the type-system macro's `null_when:
ciborium::Value::Null`), so SQLite participates in vantage-table's backend-agnostic set-invariant
enforcement (see 0.6.6): a row written into an equality-scoped set, such as a traversed has-many
relation, carries its foreign key automatically.
## 0.6.2
### Added
- `SqliteOperation`, `PostgresOperation` and `MysqlOperation` gained `not_in` and `not_in_list`,
mirroring the existing `in_` / `in_list` pair.
## 0.6.1
- SQLite Vista now implements `fetch_window` (advertised via `can_fetch_window`), serving an
arbitrary `[offset, offset + limit)` row window through `Pagination::window`. Previously only
page-indexed `fetch_page` was available, so random-access window fetches were refused.
- Regression test `vista_get_ref_preserves_with_expression_columns` covers `vantage-table` 0.6.4's
fix for computed columns surviving `get_ref` entity erasure — a typed child with a
`with_expression` now keeps that column when reached through a parent vista's `get_ref`.
## 0.6.2 — 2026-06-21
- Coordinated 0.6 release; internal dependencies realigned to 0.6. No public API changes.
## 0.5.9 — 2026-06-07
### Changed
- `register_engine!` is split so its registrations live in a reusable
`__register_engine_onto(&mut Engine)`; the macro and `__create_engine` call it. Prepares the SQL
backend for the conventional Rhai-scripted reference traversal added in `vantage-vista` 0.5.4. No
behavior change for existing engine call sites.
## 0.5.8 — 2026-06-06
### Changed
- Tracks the `vantage-vista` thin refactor (0.5.3): dropped the obsolete `with_foreign` vista
integration test now that cross-persistence traversal lives in `vantage-vista-factory`. No
functional change to the SQL backends.
## 0.5.7 — 2026-06-02
### Added
- Tables can be sourced from a sub-`SELECT` via `vantage-table`'s new `SelectSource`
(`type Source = SelectSource<SqliteSelect>`), rendering `FROM (<select>) AS <alias>`.
- SQLite vista specs accept a `sqlite.rhai:` block: a Rhai script that builds the vista's source
`SELECT` instead of pointing at a physical table. The resulting vista is read-only
(insert/update/delete capabilities are cleared). Requires the `rhai` feature.
## 0.5.6 — 2026-06-01
### New Features
- **Rhai DSL Engine**: Write SQL queries in Rhai scripting language with full cross-database
support. The new `rhai` feature flag enables a high-level DSL that compiles to vendor-specific SQL
for SQLite, PostgreSQL, and MySQL. Example:
```rust
let users = table("users").alias("u");
select()
.from(users)
.expression(users["name"])
.where(users["age"] >= 18)
.order_by(users["name"], "asc")
```
- Automatic identifier quoting (backticks for MySQL, double quotes for PostgreSQL/SQLite)
- Dialect-aware primitives: `date_format()` translates to `strftime()`/`TO_CHAR()`/`DATE_FORMAT()`
- New `group_concat()` primitive with DISTINCT support (maps to `GROUP_CONCAT`/`STRING_AGG`)
- Comparison operators (`==`, `!=`, `<`, `>`, `<=`, `>=`) work across all backends
- Test runner with `--fix` mode for generating SQL snapshots
- **GroupConcat Primitive**: Cross-database string aggregation with optional DISTINCT. Renders as:
- SQLite/MySQL: `GROUP_CONCAT(DISTINCT expr, ',')`
- PostgreSQL: `STRING_AGG(DISTINCT expr, ',')`
### Internal Changes
- Added `SelectBuilder` and `JoinBuilder` traits for database-specific select/join operations
- Refactored select builder methods into dedicated module (`src/rhai_engine/select_methods.rs`)
- Implemented comparison operators module (`src/rhai_engine/operators.rs`)
- New test infrastructure: `examples/rhai_test.rs` runner with snapshot testing support
- Added `tests/rhai-tests/` directory with `.rhai` query files and `.sql`/`.err` snapshots for all
three backends
## 0.5.5 — 2026-05-31
- Contained relations on SQLite, PostgreSQL, and MySQL: embedded collections stored as JSON columns
surface as editable sub-Vistas, with eager writeback patching the host column. Postgres and MySQL
share the SQLite path verbatim. Also lowers a YAML `contained:` section in `table_from_spec`. See
the
[contained relations guide](https://romaninsh.github.io/vantage/new-persistence/step9-contained-relations.html).
## 0.5.4 — 2026-05-30
- The SQLite, PostgreSQL, and MySQL shells implement
[`TableShell::get_ref_target`](https://docs.rs/vantage-vista/0.5.1/vantage_vista/trait.TableShell.html),
and their factories populate `VistaMetadata::references` — enabling
[vantage-vista 0.5.1](https://docs.rs/vantage-vista/0.5.1/vantage_vista/)'s nested insert through
relations. Tracks [vantage-table 0.5.4](https://docs.rs/vantage-table/0.5.4/vantage_table/).
## 0.5.3 — 2026-05-23
- Align all internal dependency versions to 0.5+. No public API changes.
## 0.5.2 — 2026-05-23
- Drops the `vantage_table::any::AnyTable` re-export from `prelude` — `AnyTable` is deleted upstream
in [vantage-table 0.5.2](https://docs.rs/vantage-table/0.5.2/vantage_table/). Use the driver's
`vista_factory().from_table(...)` for cross-driver wrapping.
## 0.5.1 — 2026-05-23
- Tracks [vantage-dataset 0.5.0](https://docs.rs/vantage-dataset/0.5/vantage_dataset/)'s `ImTable` /
`ImDataSource` parametrization. No public API change in this crate.
## 0.5.0 — 2026-05-23
- Bumped to the 0.5 line to track
[vantage-table 0.5.0](https://docs.rs/vantage-table/0.5.0/vantage_table/)'s opening of the
`AnyTable` decommission cycle. No code changes beyond the dependency pin.
## 0.4.9 — 2026-05-18
- Tracks [vantage-vista 0.4.10](https://docs.rs/vantage-vista/0.4.10/vantage_vista/)'s
schema-on-source refactor. Each SQL `*TableShell` now owns its
[`VistaMetadata`](https://docs.rs/vantage-vista/0.4.10/vantage_vista/struct.VistaMetadata.html)
and implements the new `columns` / `references` / `id_column` shell methods. Factory entry points
(`db.vista_factory().from_table(...)` / `from_yaml(...)`) are unchanged.
- Pins `vantage-vista = "0.4.10"`.
## 0.4.8 — 2026-05-17
- All three SQL `*TableShell`s ship the full Stage 5 query surface:
[`add_order`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/struct.Vista.html#method.add_order)
on any column (every column gets the
[`ORDERABLE`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/flags/constant.ORDERABLE.html)
flag at factory time),
[`add_search`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/struct.Vista.html#method.add_search)
via the existing `search_table_condition`, and offset-style pagination
([`set_page_size`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/struct.Vista.html#method.set_page_size) +
[`fetch_page`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/struct.Vista.html#method.fetch_page)
/
[`fetch_next`](https://docs.rs/vantage-vista/0.4.9/vantage_vista/struct.Vista.html#method.fetch_next),
encoding the cursor as a 1-based page number).
- Capabilities updated: `can_order`, `can_search`, `can_set_page_size`, `can_fetch_page`,
`can_fetch_next` all `true`. The retired `paginate_kind` flag is gone — drop it from any direct
`VistaCapabilities` construction.
- Pins `vantage-vista = "0.4.9"`, `vantage-table = "0.4.12"`.
## 0.4.7 — 2026-05-16
- Internal dependency version refresh; no public API changes.
## 0.4.6 — 2026-05-16
- All three SQL `*TableShell`s implement
[`TableShell::get_ref`](https://docs.rs/vantage-vista/0.4.7/vantage_vista/trait.TableShell.html#method.get_ref)
and `get_ref_kinds`: row-based reference traversal at the Vista layer. Each shell converts the
CBOR parent row into the driver's `Any*Type` map, calls `Reference::resolve_from_row` on the
wrapped typed table, and re-wraps the result via the driver's own `VistaFactory`.
- `eq_value_condition` implemented on `SqliteDB`, `PostgresDB`, `MysqlDB` via their respective
`*Operation::eq` traits, returning the driver's native condition type.
- Integration tests in `tests/sqlite/6_vista.rs` exercise the new path end-to-end against in-memory
SQLite: same-driver `has_many` traversal, `Vista::list_references` cardinality, and the
`Vista::with_foreign` lazy-closure invariant.
- Pins `vantage-vista = "0.4.7"`, `vantage-table = "0.4.10"`.
## 0.4.5 — 2026-05-09
- Pins `vantage-types` to `>= 0.4.2`. The `RichText`-returning `TerminalRender` impls landed in
0.4.4 alongside `vantage-types 0.4.2`; without an explicit floor, cargo could resolve
`vantage-types` to 0.4.0/0.4.1 and fail to compile against the old trait shape.
## 0.4.4 — 2026-05-04
- New optional `vista` feature wires SQLite, Postgres, and MySQL into
[`vantage-vista`](https://docs.rs/vantage-vista). Call `db.vista_factory().from_table(table)` to
expose any typed `Table<…>` as a `Vista`, or load a YAML spec via `build_from_spec` for
config-driven setups.
- Each backend ships its own `*VistaSpec` / `*VistaFactory` / `*TableShell` triple under
`mysql::vista`, `postgres::vista`, and `sqlite::vista`, with full read/write/count capabilities
and `eq` filtering through the existing typed-column path.
- Backend-specific `sqlite:` / `postgres:` / `mysql:` blocks in the YAML spec let you override table
and column names without leaving the spec.
- `from_table` now preserves the original entity type instead of erasing to `EmptyEntity` —
`Table<Db, E>` survives the wrap so user-defined `with_expression` closures parameterised over `E`
still typecheck. The boxed `TableShell` in `Vista` keeps the dyn-erasure boundary at one place.
- `*TableShell` implements
[`driver_name`](https://docs.rs/vantage-vista/0.4.4/vantage_vista/trait.TableShell.html#method.driver_name)
so
[`Vista::driver()`](https://docs.rs/vantage-vista/0.4.4/vantage_vista/struct.Vista.html#method.driver)
reports `"sqlite"` / `"postgres"` / `"mysql"` for diagnostics.
- Bumps minimum [`vantage-vista`](https://docs.rs/vantage-vista/0.4.4/) requirement to 0.4.4.
## 0.4.3 — 2026-04-19
- SQL `is_null` / `is_not_null` operations rendered as `{} IS NULL` / `{} IS NOT NULL` for sqlite,
postgres, mysql.
- Doc fixes in `docs4`.