A new query leaf form must be handled in every place that reads query leaves
Symptom#
After adding any(path) (array membership, stage 7c), the View returned the right rows, but the reader and the Arrow reader returned 0 rows for the same query. Other leaf consumers could misread the new leaf as a plain path.
Cause#
The query AST is read in many places, and each has its own code for a leaf:
- the two evaluators (the JSON-DOM evaluator and the other evaluator);
- the reader's ondemand value maps (
store_positions,store_referenced) in the trace reader and in the Arrow reader; - the raw pre-filter (
ranges_of, needles); - the chunk pruner (
ChunkPruner, with a per-file rewrite ofany()to an OR over catalog positions); - subsumption, the mask, the aggregation tier and the Conditions (
is_leaf).
The reader paths do not go through the View's evaluator, so a leaf form that only the evaluators know matches nothing there.
Fix#
When you add or change a leaf form (a new FieldNode flag, operator or node type), grep for every consumer of FieldNode/CompareNode/field.path and decide for each one: support it, or decline it (treat the leaf as residual, no pruning). For any(): the evaluators, the reader value maps and the pruner support it; subsumption, the mask, the aggregation tier and the Conditions decline it; the pre-filter skips it.
Test the new form through each read path: View collect, sink_json, the reader, the Arrow reader, and with the index on and off.
Evidence#
- Stage 7c (
array-membership, commit9668d017 feat(query): filter array elements with any(path)): the reader and Arrow reader returned 0 rows until their value maps handledany(); teststest_any_view.cppand the parser, evaluator, builder, subsumption, fuzz and Python tests cover the paths.