gregfelice opened a new issue, #2479:
URL: https://github.com/apache/age/issues/2479

   Splitting this out of the review discussion on #2468 so it can be tracked 
independently of that PR's merge.
   
   ### Summary
   
   Relationship-type alternation (`[:A|B]`, added in #2468) is implemented as a 
post-filter over the generic edge parent table rather than as a scan restricted 
to the listed labels. The result is that `MATCH ()-[:A|B]->()` reads every edge 
label table in the graph, not just `A` and `B`.
   
   ### Mechanism
   
   1. The parser action sets `rel->label` to `NULL` for an alternation pattern 
(the PR's own comment on `make_edge_label_alternation_qual` states this 
explicitly).
   2. With a NULL label, `transform_cypher_edge` resolves the edge to 
`AG_DEFAULT_LABEL_EDGE` (`cypher_clause.c:5908`, `:5914`) and builds the 
`RangeVar` at `_ag_label_edge` (`cypher_clause.c:6021-6030`).
   3. Every user-created edge label table inherits from `_ag_label_edge` 
(`label_commands.c:286-289`, applied at `:407`), and the `RangeVar` carries 
`inh = true` (`makeRangeVar` default, passed through at 
`cypher_clause.c:6033-6034`). Postgres therefore expands the scan across all 
edge label child tables.
   4. The alternation is then narrowed by a synthetic qual of the form 
`ag_catalog._extract_label_id(<edge>.id) IN (id_A, id_B, ...)`.
   
   Because step 4 is a function-wrapped expression over `id` rather than a 
predicate on a column Postgres can reason about, neither constraint exclusion 
nor partition pruning can eliminate the non-matching children. The non-matching 
tables are scanned and then discarded row by row.
   
   ### Impact
   
   `[:A|B]` costs O(total edge labels in the graph) instead of O(2). On a graph 
with labels `A..E` the query touches all five tables; the gap widens linearly 
with edge-label cardinality. Graphs with many edge types — a common modeling 
style — pay the most.
   
   This is a performance characteristic of the new feature, not a correctness 
bug: results are correct, only the plan is wider than necessary.
   
   ### Suggested direction
   
   Build an Append/Union over the resolved labels' own `RangeVar`s, reusing the 
per-label lookup the single-label path already performs 
(`get_label_relation_name`, `cypher_clause.c:6023`), instead of falling through 
to the generic parent plus post-filter. That keeps the scan proportional to the 
number of labels actually named in the pattern.
   
   An alternative worth considering is keeping the current structure but 
emitting a prunable predicate, so the planner can exclude children without the 
function wrapper.
   
   ### Notes
   
   - Related review discussion: #2468. @jrgemignani raised a performance 
question on that PR (Jul 13) that points at this same behavior.
   - Filing this does not by itself resolve the concern raised on #2468 — that 
review asked for either a fix or a scope-out accompanied by a benchmark 
quantifying the regression at realistic label counts. This issue tracks the 
underlying problem regardless of which path #2468 takes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to