Column order is not load-bearing for table rows, and the docs say it is #32

Closed
opened 2026-08-19 15:42:14 +00:00 by yuris · 0 comments
Owner

The generator, the README and a test all assert that column order is what gives row fields their names, on the grounds that rows are positional JSON arrays. Captured wire data from a live 2.8.2 server says table rows are named objects.

Evidence

dev/test/fixtures/session.jsonl, first inserted row of an InitialSubscription:

{"id":1,"sender":"yuris","text":"seeded before subscribe"}

A named object. Meanwhile gen-schema.sh opens with:

# Why this exists: TransactionUpdate rows arrive as *positional* JSON arrays, so
# decoding them needs the column order the module declared.

and stamps the same claim into every generated file:

-- Do not edit. Column ORDER is load-bearing: TransactionUpdate rows are
-- positional arrays, so this order is how fields get their names.

The decoder is already right — protocol.decode_row checks for any known column name and returns the object as-is, falling back to positional only when none matches (stdb/protocol.lua:156-165). So there is no bug in the code. The doctrine around it is wrong, and it is load-bearing doctrine: it justifies the generator's existence, the "reorder a column and every client mis-maps fields" warning, and a fixture test asserting column order.

What is actually true

From v2.8.2 source: table rows serialize through RowRef's schema-aware impl (crates/table/src/table.rs:2034), producing a named product. Views project through a different path that produces positional arrays. So the positional branch is not dead code — it is the view path, and possibly older servers — but it is not what a normal table subscription hits.

Work

  • Determine empirically which outputs are positional: views, one_off_query results, SubscribeApplied vs SubscribeMultiApplied, and older ?version= schema endpoints. Capture a fixture of each rather than reasoning about it
  • Correct the comments in gen-schema.sh, the generated file header and the README to say what is true, including why the positional fallback still exists
  • Keep the column-order assertion in the fixture test only if some captured output genuinely depends on it — and if so, name that output in the test

Why bother, given nothing is broken

Because the reason to keep the generator is still excellent and currently mis-stated. Column order is needed for the positional cases, and once typed codegen lands, column types are needed for decoding — a far stronger justification than the one in the file. A comment that confidently explains a mechanism which does not apply is how the next person builds on a false premise, and this repo is otherwise unusually careful about exactly that.

Priority

Low effort, and it should land before 1.0 simply because for a library the README is the product.

The generator, the README and a test all assert that column **order** is what gives row fields their names, on the grounds that rows are positional JSON arrays. Captured wire data from a live 2.8.2 server says table rows are named objects. ## Evidence `dev/test/fixtures/session.jsonl`, first inserted row of an `InitialSubscription`: ```json {"id":1,"sender":"yuris","text":"seeded before subscribe"} ``` A named object. Meanwhile `gen-schema.sh` opens with: ``` # Why this exists: TransactionUpdate rows arrive as *positional* JSON arrays, so # decoding them needs the column order the module declared. ``` and stamps the same claim into every generated file: ``` -- Do not edit. Column ORDER is load-bearing: TransactionUpdate rows are -- positional arrays, so this order is how fields get their names. ``` The decoder is already right — `protocol.decode_row` checks for any known column name and returns the object as-is, falling back to positional only when none matches (`stdb/protocol.lua:156-165`). So there is no bug in the code. The doctrine around it is wrong, and it is load-bearing doctrine: it justifies the generator's existence, the "reorder a column and every client mis-maps fields" warning, and a fixture test asserting column order. ## What is actually true From v2.8.2 source: table rows serialize through `RowRef`'s schema-aware impl (`crates/table/src/table.rs:2034`), producing a **named** product. Views project through a different path that produces positional arrays. So the positional branch is not dead code — it is the view path, and possibly older servers — but it is not what a normal table subscription hits. ## Work - [ ] Determine empirically which outputs are positional: views, `one_off_query` results, `SubscribeApplied` vs `SubscribeMultiApplied`, and older `?version=` schema endpoints. Capture a fixture of each rather than reasoning about it - [ ] Correct the comments in `gen-schema.sh`, the generated file header and the README to say what is true, **including why the positional fallback still exists** - [ ] Keep the column-order assertion in the fixture test only if some captured output genuinely depends on it — and if so, name that output in the test ## Why bother, given nothing is broken Because the reason to keep the generator is still excellent and currently mis-stated. Column order is needed for the positional cases, and once typed codegen lands, column **types** are needed for decoding — a far stronger justification than the one in the file. A comment that confidently explains a mechanism which does not apply is how the next person builds on a false premise, and this repo is otherwise unusually careful about exactly that. ## Priority Low effort, and it should land before 1.0 simply because for a library the README *is* the product.
yuris closed this issue 2026-08-19 17:48:24 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/spacetimedb-defold#32
No description provided.