mvx v0.2.0
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:
WITHmatches 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 BYpushes down on an unmapped attribute when the sort is numeric, reproducing MV's own ordering.CREATE-INDEXworks on an unmapped attribute on mysql.DESCRIBEreports the query that will actually run, including a plainBYand 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/Nitems in VOC, run by typing their name, plus.C,.COand.X name. - Arithmetic and aggregate I-types —
PRICE * QTYandSUM(EPRICE)evaluate instead of listing blank, per value across an association. STOP "message"prints and stops, andABORTexists. The concatenated form used to abort the compiler with invalid IR.@SENTENCEis populated, so portable code needs no$IFDEFbetween it andSENTENCE().- Packages can export a
FUNCTION, resolved across a package boundary byDEFFUN. mvx --version, theVERSIONverb,MVXVERSION()andSYSTEM(1001)— the release and the driver ABI. A package can declare!mvx>=x.y.zor!mvx-abi>=n, andLINK-PKGrefuses what it cannot satisfy.- The package submodules are gone; the JSON codec is built into the runtime.
What's Changed
- #136 a package declares its verbs, and mkpkg derives the VOC record by @gheydon in #139
- #142 PLATFORM.H says it is generated, and names the master file by @gheydon in #143
- #130 dynamic arrays: the index is reached, believed, and kept by @gheydon in #144
- #145 COUNT and DCOUNT ask the index instead of scanning by @gheydon in #145
- #146 INSERT and DELETE stop rebuilding the value by @gheydon in #146
- #147 the reductions stop paying for generality they do not use by @gheydon in #147
- #148 a benchmark for the sort inside the SORT and LIST verbs by @gheydon in #148
- #149 the repository workflow mv_git ended up with by @gheydon in #149
- #150 A SQLite storage backend, and FILELIST stops naming drivers by @gheydon in #151
- #152 A MySQL / MariaDB storage backend by @gheydon in #154
- #155 One logical WRITE is one transaction (driver ABI 11) by @gheydon in #156
- #158 one column per attribute, and a deterministic read-back by @gheydon in #159
- #160 don't truncate the package path, don't hash the checkout path by @gheydon in #161
- #131 cover what
mvx-git adoptasks about the open form by @gheydon in #132 - #164 phrases, a default column list, and a dictionary listing worth reading by @gheydon in #165
- demo-tests.sh belongs with the account it tests by @gheydon in #163
- Bump the demo submodule so its suite is reachable by @gheydon in #166
- publish-source: take an artifact base, like the build actions do by @gheydon in #167
- Merge develop into main: publish-source takes an artifact base by @gheydon in #168
- #169 drop the package submodules; JSON is built into the runtime by @gheydon in #170
- #122 a test system for the demo account, outside it by @gheydon in #176
- #157 store records as documents, not blobs by @gheydon in #175
- #114 the R83 command stack: .L, .R, .X, .DE by @gheydon in #178
- #177 TCL macros, and the stack's .C / .CO / .X name by @gheydon in #179
- #121 arithmetic and aggregate I-types are evaluated by @gheydon in #180
- #120 STOP takes a message, and ABORT exists by @gheydon in #181
- #130 a literal operand is boxed once, not per iteration by @gheydon in #182
- #101 a package can export a FUNCTION, and DEFFUN resolves it by @gheydon in #184
- #117 the toolchain says what it is, and a package says what it needs by @gheydon in #185
- #97 @Sentence is populated, so one spelling is portable by @gheydon in #186
Full Changelog: v0.1.4...v0.2.0
What's Changed
- #136 a package declares its verbs, and mkpkg derives the VOC record by @gheydon in #139
- #142 PLATFORM.H says it is generated, and names the master file by @gheydon in #143
- #130 dynamic arrays: the index is reached, believed, and kept by @gheydon in #144
- #145 COUNT and DCOUNT ask the index instead of scanning by @gheydon in #145
- #146 INSERT and DELETE stop rebuilding the value by @gheydon in #146
- #147 the reductions stop paying for generality they do not use by @gheydon in #147
- #148 a benchmark for the sort inside the SORT and LIST verbs by @gheydon in #148
- #149 the repository workflow mv_git ended up with by @gheydon in #149
- #150 A SQLite storage backend, and FILELIST stops naming drivers by @gheydon in #151
- #152 A MySQL / MariaDB storage backend by @gheydon in #154
- #155 One logical WRITE is one transaction (driver ABI 11) by @gheydon in #156
- #158 one column per attribute, and a deterministic read-back by @gheydon in #159
- #160 don't truncate the package path, don't hash the checkout path by @gheydon in #161
- #131 cover what
mvx-git adoptasks about the open form by @gheydon in #132 - #164 phrases, a default column list, and a dictionary listing worth reading by @gheydon in #165
- demo-tests.sh belongs with the account it tests by @gheydon in #163
- Bump the demo submodule so its suite is reachable by @gheydon in #166
- publish-source: take an artifact base, like the build actions do by @gheydon in #167
- Merge develop into main: publish-source takes an artifact base by @gheydon in #168
- #169 drop the package submodules; JSON is built into the runtime by @gheydon in #170
- #122 a test system for the demo account, outside it by @gheydon in #176
- #157 store records as documents, not blobs by @gheydon in #175
- #114 the R83 command stack: .L, .R, .X, .DE by @gheydon in #178
- #177 TCL macros, and the stack's .C / .CO / .X name by @gheydon in #179
- #121 arithmetic and aggregate I-types are evaluated by @gheydon in #180
- #120 STOP takes a message, and ABORT exists by @gheydon in #181
- #130 a literal operand is boxed once, not per iteration by @gheydon in #182
- #101 a package can export a FUNCTION, and DEFFUN resolves it by @gheydon in #184
- #117 the toolchain says what it is, and a package says what it needs by @gheydon in #185
- #97 @Sentence is populated, so one spelling is portable by @gheydon in #186
Full Changelog: v0.1.4...v0.2.0