vantage-sql 0.6.23

Vantage extension for SQL databases (Postgres, MySQL, SQLite)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# Changelog

## 0.6.23 — 2026-09-08

- `add_op_condition` handles `FilterOp::Like`: `LIKE '%…%' ESCAPE '$'` on
  SQLite and MySQL, `ILIKE` on Postgres — matching what quicksearch already
  does on each dialect.
- The `%…%` escape lives in one place (`like_pattern_str`), shared by
  quicksearch and by the new operator, so the two cannot drift.

## 0.6.22 — 2026-09-05

- The `rhai` feature runs on a `vantage-rhai` host: `register_engine!` emits
  a `SqlVocab` for the dialect plus one lazily built, bounded host
  (`__host()`), and `__create_engine` is gone. Query-source scripts compile
  through the host's cache — a table's script parses once, not per build.
- `eval_to_select_args` binds `args` and `base` through an `Env`;
  `eval_to_select` delegates to it. The `rhai` crate is reached as
  `vantage_sql::rhai_engine::rhai` (re-exported from `vantage-rhai`), so a
  consumer no longer needs its own `rhai` dependency to match versions.
- An unknown identifier in a script now reads "unknown name `x`" (the host's
  message) rather than rhai's variable-not-found text.

## 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`.