Skip to content

Defer index parser selection until full operator context is available #7091

Description

@ztorchan

#7072 fixed an issue where multiple JSON indices on different paths of the same column would route queries to the wrong sub-parser, by introducing MultiQueryParser::select() and calling it at the point of column reference extraction in maybe_indexed_column. That fix works correctly for the JSON multi-path scenario because the column reference itself (e.g. json_extract(json, '$.b')) carries enough information to disambiguate which sub-parser handles it.

However, the fix also surfaces a more general architectural limitation: maybe_indexed_column selects a parser based solely on the column reference — before the full operator context (e.g. >, <, =, IS NULL) is known. When that context matters for parser selection, the wrong parser can be chosen, causing index acceleration to be missed entirely.

Concrete case: Suppose column x has both a bloom filter index and a btree index, and the query is x > 7:

  1. visit_comparison calls maybe_indexed_column(&expr.left) — only Column("x") is visible at this point
  2. MultiQueryParser::select() asks each child parser's is_valid_reference — both bloom and btree accept a bare Column("x") as valid
  3. select() returns whichever child was registered first (e.g., bloom)
  4. visit_comparison then calls bloom.visit_comparison("x", 7, Operator::Gt) — bloom returns None because it only supports equality
  5. The query falls through to a full-table scan, even though btree could have handled x > 7

If the parser selection had been deferred until after visit_comparison knew both the column and the operator (>), the btree index would have been selected.

The root cause is that maybe_indexed_column does not distinguish between two qualitatively different decisions:

  • Reference validation ("does this index handle this column at all?") — appropriate for deciding which JSON path parser to use, where the column reference alone carries enough information
  • Capability matching ("can this index serve this specific query shape?") — requires knowing the operator, which only exists at the parent expression node level

Conflating the two means the first valid parser wins, even if it cannot actually serve the query.

This was previously noted in the existing TODO comment on MultiQueryParser::is_valid_reference:

This is maybe not quite right. We should filter down the list of parsers based on those that consider the reference valid… This will be a problem if the user creates two indexes (e.g. btree and json) on the same column and those two indexes have different reference schemes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions