// MATCH-scope relationship-uniqueness marking (port of the Rust // recognize.rs::mark_rel_uniqueness): within one MATCH clause (= one // scope, spanning its comma patterns and any planner splits) every // relationship must be pairwise distinct -- ISO GQL's DIFFERENT EDGES // default match mode / openCypher's relationship isomorphism.
package plan // markRelUniqueness marks each rel-binding op's uniqueness participation. // Tracking is gated on relationship-TYPE intersection: two ops whose type // sets cannot name the same relationship (disjoint, non-empty) never // conflict, so a typical multi-hop clause over distinct types stays // completely untracked and zero-cost. An op with an intersecting op // EARLIER in execution order gets Check (its candidate must not reuse a // used pair); one with an intersecting op LATER gets Contribute (it // pushes its pair(s)). A contributing var-length loses DedupEndpoints // (trails with different rel sets must stay distinct rows for the // downstream exclusion to be exact). func markRelUniqueness( []Stage) { type struct { uint32 []string , int } var [] for , := range { , := .(*MatchStage) if ! || .Walk { // REPEATABLE ELEMENTS: walk semantics, no uniqueness pairs. continue } for := range .Ops { := &.Ops[] if .Kind == OpExpand || .Kind == OpVarExpand { = append(, {: .Scope, : .Types, : , : }) } } } for := range { := &[] , := false, false for := range [:] { if []. == . && intersects(., [].) { = true break } } for := + 1; < len(); ++ { if []. == . && intersects(., [].) { = true break } } if ! && ! { continue } := [.].(*MatchStage) := &.Ops[.] .Uniq = &RelUniq{Scope: ., Check: , Contribute: } if .Kind == OpVarExpand && { .DedupEndpoints = false } } } // intersects reports whether two type lists can name the same relationship. // An empty type list matches any relationship, so it intersects everything. func intersects(, []string) bool { if len() == 0 || len() == 0 { return true } for , := range { for , := range { if == { return true } } } return false } // remarkRelUniqueness recomputes every tracked op's Check/Contribute over // the FINAL stage list's EFFECTIVE execution order: hash-join extraction // moves the consumed connecting expand (the probe) and the collected build // ops relative to the ops left in place, so flags assigned on the nested // order can invert -- an op that was the last of its scope (Check-only) // can come to execute FIRST as the join's probe, pushing nothing while the // op now after it contributes to nobody, and a reused relationship sails // through. Extraction never changes WHICH ops intersect (only their // order), so the tracked set is exactly the ops already carrying Uniq; // their recorded scope survives the move. Effective order is stage order // with a hash join expanding to its build stages' ops then its probe // (builds materialize before any probe emission). func remarkRelUniqueness( []Stage) { type struct { uint32 []string *BindOp } var [] := func( *MatchStage) { if .Walk { return } for := range .Ops { if := &.Ops[]; .Uniq != nil { = append(, {: .Uniq.Scope, : .Types, : }) } } } for , := range { switch s := .(type) { case *MatchStage: () case *HashJoinStage: for , := range .Build { () } if .Probe.Uniq != nil { = append(, {: .Probe.Uniq.Scope, : .Probe.Types, : &.Probe}) } } } for := range { := &[] , := false, false for := range [:] { if []. == . && intersects(., [].) { = true break } } for := + 1; < len(); ++ { if []. == . && intersects(., [].) { = true break } } ..Uniq = &RelUniq{Scope: ., Check: , Contribute: } if ..Kind == OpVarExpand && { ..DedupEndpoints = false } } }