Skip to content

mvx v0.2.0

Choose a tag to compare

@gheydon gheydon released this 09 Sep 05:09
· 71 commits to main since this release

Records are stored as documents, not blobs

This release changes how records are stored, and existing files must be converted.

A record used to go into every SQL backend as one opaque blob column, so the backend could not see a single field of it. It is now a document: attributes become JSON keys, multivalues become arrays, subvalues nest inside those.

backend before now
sqlite blob doc TEXT
postgres blob doc jsonb
mysql blob doc JSON
mongo a wrapped blob a real BSON subdocument

Values are stored as text and cast at query time, so the document holds what you wrote and the query decides what it means. A value that is not valid UTF-8 is carried as {"$b64": ...} rather than corrupted.

Migrating

Existing files are not readable by this release until they are converted:

mvx-doc-migrate <driver> <location>

It converts every file in a location in place, one transaction per file, and is safe to re-run. The location is written the way BINDINGS writes it — for a connection profile that is @name, so run it from the account. An old file cannot simply be opened, which is why the tool walks the backend's own catalogue rather than asking MVX for a file list.

Files now also record what format they are, so a file can say what it is instead of being guessed at.

Why it was worth breaking

Once the backend can see inside a record, the query goes to the data instead of the data coming to the verb:

  • WITH matches any value of a multivalued attribute, and gives the same answer whether an index exists or not — previously the indexed and unindexed paths disagreed.
  • ORDER BY pushes down on an unmapped attribute when the sort is numeric, reproducing MV's own ordering.
  • CREATE-INDEX works on an unmapped attribute on mysql.
  • DESCRIBE reports the query that will actually run, including a plain BY and unmapped attributes.

Not everything pushes. On an unmapped attribute:

= # ranges (> < >= <=)
sqlite pushes pushes pushes
postgres pushes pushes pushes
mysql pushes pushes verb
mongo pushes verb verb

mysql is the odd one because JSON_TABLE does not correlate inside EXISTS, so its predicate is built from JSON_CONTAINS, which expresses containment and its negation but not an ordering. mongo also has no select_order or explain, so every BY sorts in the verb. Every one of these returns the right answer, just without the backend's help — a mapped column has none of these limits.

Driver ABI 11 → 14

Any out-of-tree storage driver must be rebuilt. Compiled artifacts now carry the ABI they were built against and are refused at load with a clear message, rather than failing later as an undefined symbol.


Also in this release

  • The R83 command stack — .L, .R, .RU, .DE, .X, .?, with D3's semantics: unique commands only, entry 1 is the most recent, and re-running or editing an entry moves it to the top. One history, shared with the line editor.
  • TCL macros — M/N items in VOC, run by typing their name, plus .C, .CO and .X name.
  • Arithmetic and aggregate I-types — PRICE * QTY and SUM(EPRICE) evaluate instead of listing blank, per value across an association.
  • STOP "message" prints and stops, and ABORT exists. The concatenated form used to abort the compiler with invalid IR.
  • @SENTENCE is populated, so portable code needs no $IFDEF between it and SENTENCE().
  • Packages can export a FUNCTION, resolved across a package boundary by DEFFUN.
  • mvx --version, the VERSION verb, MVXVERSION() and SYSTEM(1001) — the release and the driver ABI. A package can declare !mvx>=x.y.z or !mvx-abi>=n, and LINK-PKG refuses what it cannot satisfy.
  • The package submodules are gone; the JSON codec is built into the runtime.

What's Changed

Full Changelog: v0.1.4...v0.2.0

What's Changed

Full Changelog: v0.1.4...v0.2.0