Dynoxide 0.12.0: PartiQL RETURNING, wasm conformance
Dynoxide 0.12.0 is out. PartiQL now honours RETURNING, and the browser build has been through the conformance suite for the first time. There's a stack of smaller fixes below, and one breaking change if you build against the Rust crate.
PartiQL RETURNING
Dynoxide has been parsing the RETURNING clause since PartiQL landed, and then dropping it on the floor. DELETE FROM Music WHERE artist = 'Pixies' RETURNING ALL OLD * parsed cleanly, deleted the item, and handed you back nothing.
It works now. DELETE ... RETURNING ALL OLD * gives you the deleted item in Items, and a present but empty Items array if the target wasn't there, which is what DynamoDB does over PartiQL rather than what the classic DeleteItem path does.
UPDATE ... RETURNING <ALL|MODIFIED> <OLD|NEW> * returns the matching projection. The MODIFIED variants are the fiddly ones: they return only the paths that actually changed, so a nested SET a.b = :v gives back the changed leaf rather than the whole a attribute. They exclude the primary key, and return an empty Items array when nothing was projected.
BatchExecuteStatement honours RETURNING on each member, ExecuteTransaction rejects one with a top-level ValidationException, and the three variants DynamoDB won't take on a DELETE (MODIFIED OLD *, ALL NEW *, MODIFIED NEW *) are now rejected with its exact validation message instead of ignored. (#137)
The browser build on the conformance board
At the end of the 0.11.0 post I said the next job was holding the browser engine to the same standard as the native one. It's in the 24 July run as its own target, scored separately rather than folded into native dynoxide.
100% across all three tiers, on the 785 of the suite's 998 tests it implements. The other 213 are operations the browser build doesn't do, so it covers 78.7% of the suite where the native build covers 98.6%. A perfect score over the three quarters of the suite the wasm build supports.
That's the core item and table surface, query and scan, the legacy API, validation ordering and error-message fidelity. PartiQL is in the other quarter, so the RETURNING work above is native-only.
The run needed a way in, so the engine gained a dispatchHttp worker op and there's now a test-only server behind npm run wasm:serve that drives the shipping dist/ bundle in a headless Chromium. It isn't distributed and it isn't a way to run dynoxide - one page's worth of concurrency, no TLS, fresh in-memory database on every start. Details in docs/wasm.md. The package is still published as 0.12.0-preview.
Everything else
- PartiQL
UPDATEis not an upsert. On a key that doesn't exist it now fails withConditionalCheckFailedExceptionand creates nothing. It used to write the item. - Real list-index writes in PartiQL.
SET tags[0] = :vwas treated as a write to a literal map key calledtags[0]. It now updates the element, appends when the index sits at or past the end, andREMOVE tags[0]shifts the rest up. ARETURNING MODIFIEDprojection over list indices packs the changed elements densely in ascending index order. { "NULL": false }is rejected again. In 0.11.0 I made dynoxide accept it, after watching AWS drop the true-only rule. It flipped around across regions for a while and has settled on rejection everywhere. (#145)- Table configuration over MCP.
create_tableandupdate_tablenow acceptbilling_mode,provisioned_throughput,table_classandon_demand_throughput. Before this, every table created over MCP came outPROVISIONEDwith default throughput, andupdate_tablehardcoded all three to none so the engine saw an empty update. (#154, #156, #157) - MCP
describe_tableshows table configuration. Billing mode, table class and the capacity settings for the mode the table is in. They were previously only visible underraw: true. serve --schema.dynoxide serve, anddynoxidewith no subcommand, take the sameDescribeTableJSON thatimport --schematook, and create the tables on startup.- Schema import no longer drops
BillingModeandTableClass.DescribeTablewraps both in summary objects the rebuiltCreateTableRequestnever read, so an on-demand table came backPROVISIONED. A table's ownDescribeTableoutput now round-trips without degrading. (#140) - The
1 validation error detected:envelope.PutItemandUpdateItemnow carry it on exactly the request-validation families DynamoDB envelopes, and leave the data-plane, structural and limit families bare. Classified per family at the raising site rather than by matching message text, so the message is identical over HTTP, wasm, MCP and the in-process Rust API. OnDemandThroughputsemantics, captured against eu-west-2. Rejected when the effective billing mode isPROVISIONED, members at least 1,-1onUpdateTableremoves the ceiling, a partial update merges member-wise rather than replacing. One deliberate divergence: DynamoDB returnsInternalFailurefor an empty object onUpdateTableand dynoxide returns a validation error instead of emulating a 500. (#159)- Expression size limit. Over 4096 bytes is rejected with
Expression size has exceeded the maximum allowed size, measured on the raw string before substitution, across all five expression parameters. (#146) UpdateTableadding a GSI validates the new index's key attributes against the request's ownAttributeDefinitions, not the merged stored set. (#144)- PartiQL error wording. Parse errors use
Statement wasn't well formed, can't be processed: <detail>, and a statement not opening with a DML keyword reportsExpected data manipulation.BatchExecuteStatementechoesTableNameper member, and a member that fails to parse carries the short-formValidationError.
What breaks
Rust crate consumers only. partiql::parser::Statement gained a returning field on Update and Delete, and BatchStatementResponse gained table_name. Both are now #[non_exhaustive], so constructing or exhaustively matching either needs a ... The wire API and the CLI, server and MCP surfaces are unaffected.
0.12.0 is out everywhere dynoxide ships:
- npm:
npm install --save-dev dynoxide, thennpx dynoxide - Homebrew:
brew install nubo-db/tap/dynoxide - Cargo:
cargo install dynoxide-rs(crates.io, docs.rs) - Docker:
ghcr.io/nubo-db/dynoxide, mirrored best-effort to Docker Hub (nubodb/dynoxide) and ECR Public - GitHub: source and pre-built binaries, plus the
nubo-db/dynoxide/actionGitHub Action for CI