Skip to content

unordered_map::at (std::out_of_range) planning EXPLAIN of a packed filtered count when the modulo-sum predicate uses a computed expression (e.g. offset(id(b))) #868

Description

@adsharma

What happens?

Planning the following query with EXPLAIN crashes with Error: unordered_map::at (a std::out_of_range escaping as an error, not a segfault). The same query without EXPLAIN executes fine, so the failure is in plan mapping on the EXPLAIN path.

The throw originates in Schema::getGroupPos(name) (unordered_map::at) reached via:

#2 lbug::planner::Schema::getGroupPos(std::string const&) const
#3 lbug::planner::Schema::getGroupPos(lbug::binder::Expression const&) const
#4 lbug::planner::Schema::getExpressionPos(lbug::binder::Expression const&) const
#5 lbug::processor::PlanMapper::mapAggregate(lbug::planner::LogicalOperator const*)

i.e. tryMapPackedFilteredCount() (src/processor/map/map_aggregate.cpp) calls packedChildSchema->getExpressionPos(...) on the group key and on the two predicate inputs of the (lhs + rhs) % k = 0 filter. When a predicate input is a plain property (b.id) the lookup succeeds; when it is a computed expression over the nbr internal ID (offset(id(b))) the expression is not found in the packed child schema by unique name and .at() throws.

Repro

-- any database with a two-column int64 PK node table and a rel table; e.g.:
CREATE NODE TABLE MPerson(id INT64, PRIMARY KEY(id));
CREATE REL TABLE MKnows(FROM MPerson TO MPerson);
-- (populate some edges)

CALL enable_packed_path_extend=true;
EXPLAIN MATCH (a:MPerson)-[e:MKnows]->(b:MPerson)
WHERE (a.id + offset(id(b))) % 10 = 0
RETURN a.id, COUNT(*);
Error: unordered_map::at

Observed on the current main build (pre-existing; found while testing #867, which only touches this code path's gating and does not change the crash).

Conditions (verified on a 3000-node / 6750-edge MPerson/MKnows database)

# config query shape result
A enable_packed_path_extend=true + EXPLAIN keyed COUNT(*), predicate (a.id + offset(id(b))) % 10 = 0 crash (unordered_map::at)
B enable_packed_path_extend=false + EXPLAIN same OK
C enable_packed_path_extend=true + EXPLAIN same, but predicate (a.id + b.id) % 10 = 0 (plain property) OK
D enable_packed_path_extend=true + EXPLAIN global COUNT(*) (no group key), offset predicate OK
enable_packed_path_extend=true, no EXPLAIN same as A OK (executes and returns correct rows)

So all three ingredients are required: the packed-filtered-count mapping pattern (keyed count(*) over a single modulo-sum filter above a packed extend), a predicate input that is a computed expression rather than a property, and the EXPLAIN statement (non-EXPLAIN execution of the same query does not hit the throwing lookup).

Expected

Either the plan maps (the schema should contain, or the mapper should resolve, the mapped form of the predicate input expressions), or tryMapPackedFilteredCount should bail out gracefully (return nullptr) when the key/predicate-input expressions cannot be located in packedChildSchema, falling back to the regular aggregate plan.

Suggested fix direction

In tryMapPackedFilteredCount, the lookups packedChildSchema->getExpressionPos(*key) / getExpressionPos(*predicateInputs->first|second) assume the binder expressions appear verbatim in the packed child schema. For computed expressions (e.g. offset(id(b))) the planned schema holds a different expression object for the same unique name, or holds nothing at all. Guard these lookups (e.g. Schema::containsExpression(...)-style check, or try/catch around getGroupPos) and fall back to the standard aggregate mapping when they fail. It may also be worth checking why the EXPLAIN path differs from direct execution for the same query.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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