Legacy measure integration regression fixes
Follow-up to T17, requested after committing Catalog B ordering and the deferred
pagination design as b13cdc6d. Verification ran on 2026-10-17/18. This work fixes
the nine legacy failures recorded in T17 verification;
it does not implement retained-result sessions or temporary-file spilling.
Reproduction and causes
The unchanged nine generated cases failed again in standalone Docker before any
production edits: zero passed, nine failed, in 15.172 seconds. Evidence is
.omx/legacy-nine-standalone-red-v1.{log,json}. The focused invocation is
python3 .omx/legacy-nine-e2e.py nine --topology standalone --revision red-v1.
Eight cases projected indexed arrays, directly or as hidden filter inputs:
generated_projection_0, generated_having_labels,
generated_not_having_labels, and generated_pair_002/004/008/013/017.
The writer stores each indexed array element as a separate raw index value.
Storage projection retained only the last value, and the measure reader treated
that scalar as a complete data-block array. String arrays panicked in
unmarshalVarArray; integer arrays silently lost earlier elements.
The ninth case, generated_in_int_list_0, used an empty entity IN list.
Entity extraction correctly produced no matching tuples, but dispatch sent that
set to storage, which requires a series selector. Related Boolean combinations
also treated empty alternatives incorrectly during entity merging.
Once decoding worked, full verification exposed nondeterministic SELECT *
column order: the legacy transformer iterated tag and field lookup maps. This
caused one standalone and five distributed array-query result mismatches in the
first repaired run. Wildcard projection now follows schema encounter order while
retaining existing name lookup and deduplication behavior.
That run also exposed a compatibility regression in the new empty-result path.
The native case named gen: tree depth 5 deep OR exercises unsupported intersections
of alternative entity selectors, not a query-depth limit. The legacy merger can
return nil for such intersections even when the alternatives overlap. Extraction
now explicitly rejects that unsupported merge rather than claiming no rows match;
proven empty selectors and contradictory single tuples still return empty results.
The first repaired full runs passed 932/934 standalone and 928/934 distributed;
these intermediate results are retained in .omx/legacy-full-fixed-v1-summary.json.
Changes and compatibility
Storage now offers an opt-in multivalue projection alongside the existing scalar projection. Measure reads request it for indexed string/integer arrays and decode the raw elements using the schema. Element order and duplicates are retained in normal and index-mode row/batch consumers. Scalar projections retain their last-value behavior; scalar-only queries allocate no multivalue projection map. Additional retained descriptors, values and array decoding are memory-accounted.
Normal measure dispatch returns an empty result before storage when entity
selection is impossible. Entity extraction preserves empty-set identities under
AND and OR, including remaining indexed predicates. Index-mode queries do not
use these entity selectors and retain their dispatch path. Invalid predicates
still fail validation. The legacy empty bound-array rejection is unchanged.
This is a user-requested native read/dispatch correctness follow-up to the QL implementation. Protobuf, writer code and persisted formats are unchanged. Empty indexed arrays and nulls both have no stored elements in the existing format; this repair does not introduce a distinction the stored data cannot supply. The separate typed QL membership restriction on indexed arrays remains in place.
Regression coverage
TestIndexedArrayProjection reproduces the panic through the actual writer,
native index and measure projection; it failed in 0.038 seconds before the fix.
The final table covers duplicate/order preservation, escaped and empty strings,
integer extremes, null/empty inputs, sorted/unsorted reads, scalar compatibility,
row/batch consumers and budget rejection followed by cleanup and recovery.
TestDispatchEmptyEntityINReturnsEmptyWithoutStorage reproduces the exact
series is required error before the fix. Public QL-to-dispatch tests and native
entity-extraction tests cover empty/nonempty/wildcard selectors, contradictions,
Boolean combinations, indexed residuals, validation and resource release.
TestMeasureWildcardProjectionOrder checks exact tag and field order across
100 fresh public compilations and direct-transform parity. It failed before the
fix. A minimized native/public QL regression also preserves rejection of
(service='a' OR service='b') AND (service='b' OR service='c') when the native
merger cannot represent that intersection.
The independent QL generator adds six cases:
generated_empty_entity_{and,or}_{left,right,both}. All 1,695 prior case objects,
including expected results and rejection cases, remain exactly unchanged.
The resulting 1,701-case artifact has SHA256
a4f19f0995cb5c5e5009cba8d06eaeeceaba3a8a490ca25e5f7204e0e0698d44.
The preservation audit is .omx/legacy-empty-in-artifact-audit.json.
Full storage, measure, bydbql and vectorized measure-plan package tests pass, as do
focused race tests, generator determinism/artifact/parse-bind checks,
make lint and whitespace checks. Apache headers were verified for all
12 changed/new Go files: tracked headers are unchanged and new files match the
repository template. make license-check
exits successfully but reports zero checked files, so it is not used as coverage
evidence. The explicit header audit is .omx/legacy-fixes-license-headers-final.log.
Other verification evidence is
.omx/legacy-array-*-final-v1.log, .omx/legacy-empty-in-*.log,
.omx/legacy-wildcard-{green-v2,race-v1,lint-v2}.log,
.omx/legacy-empty-in-intersection-{green,race,lint}.log,
.omx/legacy-fixes-make-lint-final.log and .omx/legacy-fixes-license-check-final.log.
End-to-end verification
The focused repaired run passes all nine originals, six generated empty-selector
cases and the native unsupported-intersection regression in each topology.
Standalone selected two additional existing row/on-disk variants of that native
case: all 18 passed. Its driver initially expected 16 and reported a selection-count
assertion after the successful test run; inspection confirmed those two extra cases,
and the distributed selector was narrowed to the vectorized native case (16/16).
Evidence is .omx/legacy-regression-standalone-fixed-v2.{json,log} and
.omx/legacy-regression-distributed-fixed-v2-distributed.{json,log}.
Verification uses standalone and distributed Docker containers, each limited to four CPUs, 8 GiB, and no additional swap. The full measure selection includes all legacy and ticketed QL cases, native measure compatibility and Top-N:
| Topology | Ticketed QL | Legacy QL | Native measure | Top-N | Total passing |
|---|---|---|---|---|---|
| Standalone | 637/637 | 157/157 | 120/120 | 20/20 | 934/934 |
| Distributed | 637/637 | 157/157 | 120/120 | 20/20 | 934/934 |
Reports are .omx/legacy-full-{standalone,distributed}-fixed-v2.{json,log}
and .omx/legacy-full-fixed-v2-summary.json.
Shared storage/transformer compatibility passes on the final source:
| Topology | Stream | Trace | Property | Total passing |
|---|---|---|---|---|
| Standalone | 97/97 | 68/68 | 24/24 | 189/189 |
| Distributed | 97/97 | 68/68 | 12/12 | 177/177 |
Reports are .omx/legacy-compat-{standalone,distributed}-fixed-v2.{json,log}
and .omx/legacy-compat-fixed-v2-summary.json.
This correctness follow-up does not rerun performance benchmarks or resolve the native 32-caller admission limit, large-input RPC evidence gaps, or T02’s prior inconclusive performance acceptance. Historical T17 benchmark results remain measurements of their recorded source revision.