Repository navigation
feat(orm): column-typed result sets via cwist_orm_query_typed() (issue #354) - #389
Merged
Merged
Conversation
…#354) The socket-backed ORM produced result rows as text and let the client re-type values with strtoll/strtod heuristics: numeric-looking TEXT came back as numbers, SQL NULL was indistinguishable from empty text, INTEGER above 2^53 lost digits through the JSON double, and BLOBs had no typed path at all (the exact-i64 commit added single-scalar workarounds only). Add an additive typed path alongside the unchanged legacy helpers: - cwist_orm_query_typed() returns a cwist_orm_result_t of cwist_orm_value_t cells typed by each value's SQLite storage class (sqlite3_column_type at the producer — JSON text alone carries no column-type information the consumer could recover): INTEGER as exact int64 through the full range, REAL as double, TEXT as string, BLOB as (bytes, length), NULL a distinct type from empty TEXT. - The worker moves from sqlite3_exec() to a prepare/step statement loop; the response gains a parallel 'typed' block (columns + row arrays of {t,v,n} cells; BLOB base64 + length). Multi-statement requests still merge rows as before. The typed cell is read before the legacy text conversion because sqlite3_column_text() mutates what sqlite3_column_type() reports. - cwist_orm_result_free(), cwist_orm_result_column_index(), cwist_orm_result_cell() accessors. The legacy 'rows' payload is byte-for-byte unchanged (raw-socket dump + unmodified pre-existing tests); all ORM examples still build. tests/test_orm.c gains test_query_typed(): per-type round-trips, INTEGER > 2^53 exactness through the typed path, NULL vs '', non-UTF-8 and empty BLOBs, mixed-type row, zero-row column metadata, error paths, and a legacy-path contrast assert. make test_orm / test_orm_socket pass, make api-check OK, check_test_wiring OK, cwist audit --gate OK. Regenerates scripts/ci/malloc-baseline.txt: the two raw-allocator lines from 37a7801 (fix(gc)) that were missing there (the gate was red at HEAD), plus this change's own raw frees/allocations consistent with orm.c/orm_socket.c's existing raw-allocator style. Also regenerates the v4.0 API baseline for the new public surface.
gg582
added a commit
that referenced
this pull request
Oct 17, 2026
…#354) (#389) The socket-backed ORM produced result rows as text and let the client re-type values with strtoll/strtod heuristics: numeric-looking TEXT came back as numbers, SQL NULL was indistinguishable from empty text, INTEGER above 2^53 lost digits through the JSON double, and BLOBs had no typed path at all (the exact-i64 commit added single-scalar workarounds only). Add an additive typed path alongside the unchanged legacy helpers: - cwist_orm_query_typed() returns a cwist_orm_result_t of cwist_orm_value_t cells typed by each value's SQLite storage class (sqlite3_column_type at the producer — JSON text alone carries no column-type information the consumer could recover): INTEGER as exact int64 through the full range, REAL as double, TEXT as string, BLOB as (bytes, length), NULL a distinct type from empty TEXT. - The worker moves from sqlite3_exec() to a prepare/step statement loop; the response gains a parallel 'typed' block (columns + row arrays of {t,v,n} cells; BLOB base64 + length). Multi-statement requests still merge rows as before. The typed cell is read before the legacy text conversion because sqlite3_column_text() mutates what sqlite3_column_type() reports. - cwist_orm_result_free(), cwist_orm_result_column_index(), cwist_orm_result_cell() accessors. The legacy 'rows' payload is byte-for-byte unchanged (raw-socket dump + unmodified pre-existing tests); all ORM examples still build. tests/test_orm.c gains test_query_typed(): per-type round-trips, INTEGER > 2^53 exactness through the typed path, NULL vs '', non-UTF-8 and empty BLOBs, mixed-type row, zero-row column metadata, error paths, and a legacy-path contrast assert. make test_orm / test_orm_socket pass, make api-check OK, check_test_wiring OK, cwist audit --gate OK. Regenerates scripts/ci/malloc-baseline.txt: the two raw-allocator lines from 37a7801 (fix(gc)) that were missing there (the gate was red at HEAD), plus this change's own raw frees/allocations consistent with orm.c/orm_socket.c's existing raw-allocator style. Also regenerates the v4.0 API baseline for the new public surface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Issue #354's exact-64-bit ask landed earlier (single-scalar
_i64helpers); this delivers the remaining ask — result sets typed by column storage class, not text:cwist_orm_query_typed()→cwist_orm_result_tofcwist_orm_value_tcells (NULL / INTEGER / REAL / TEXT / BLOBwithi64/real/text/blob+blob_lenpayloads), pluscwist_orm_result_free(),cwist_orm_result_column_index(),cwist_orm_result_cell().sqlite3_column_type()in theorm_socketworker, which moves fromsqlite3_execto a prepare/step loop and emits a parallel"typed"block; BLOBs travel base64 + length). JSON text alone carries no column-type information the consumer could recover. One documented gotcha handled:sqlite3_column_text()mutates whatsqlite3_column_type()reports, so the typed cell is read first.cwist_orm_query()'srowspayload is byte-for-byte the same (raw-socket dump + all pre-existing tests pass unmodified).What this fixes
'007'stays text)Test plan
test_query_typed()intests/test_orm.c: per-type round-trips, >2^53 exactness, NULL vs'', non-UTF-8 and empty BLOBs, mixed-type row, zero-row metadata, error paths, legacy-path contrast —make test_orm/test_orm_socketpassmake api-checkOK,check_test_wiring.pyOK,cwist audit --gateOKNote for reviewers
scripts/ci/malloc-baseline.txtregeneration also absorbs the two raw-allocator lines from37a78010(fix(gc)) that were missing there — the malloc gate was red at dev HEAD until this lands.