// Segment runner: build the segment's sink chain (stages in order, then // the terminal projection/aggregation sink), push each seeded input row // through it, finalize, and apply the boundary's WHERE. This is the // streaming form of the Rust M18 fast paths -- results are identical to // the former stage-by-stage materialization (pure allocation // optimization).
package exec import ( ) // terminal is a segment's end sink: it accumulates the projection and // yields the segment's output rows. type terminal interface { rowSink finalize() [][]value.Value } // stageProfCell is one stage's PROFILE counters: per-op counts for a MATCH // stage, a single produced-row count otherwise. type stageProfCell struct { match []uint64 single *uint64 } // identPassthroughs counts pass-through boundary executions (the // engagement oracle for the identity fast path); disableIdentPassthrough // pins differential tests to the general copy-through pipeline. var ( identPassthroughs int disableIdentPassthrough bool ) // identityBoundary reports whether seg is a pure pass-through boundary: // no stages, non-aggregated, no DISTINCT, and output column i is a bare // read of input slot i for every i -- so the output rows ARE the input // rows, and ORDER BY / pagination / the boundary filter can run on them // directly instead of copying each row through the projection arena. func identityBoundary( *plan.Segment) bool { if len(.Stages) != 0 || .Proj.Aggregated || .Proj.Distinct { return false } if len(.Proj.Returns) != .RowWidth { return false } for , := range .Proj.Returns { , := .Expr.(*ast.Var) if ! { return false } if , := .Slots[.Name]; ! || != { return false } } return true } // runSegment runs one segment over its input rows, recording per-operator // produced-row counts into prof when profiling (nil = off). func runSegment( *eval.Ctx, *plan.Segment, [][]value.Value, *explain.SegProf) [][]value.Value { // A single-segment columnar-aggregate candidate fuses here (chains // spanning boundaries fuse in runBranch); PROFILE runs keep the // general pipeline so the per-operator counts describe it. if .ColAgg && == nil { if , , := tryColumnarAggChain(, []*plan.Segment{}, 0, ); && == 1 { return } } // Pass-through boundary: the inputs are the previous boundary's // materialized rows (stable -- nothing downstream mutates them), so an // identity projection re-emitting them row by row through the arena is // pure copy cost. Sort/paginate/filter the input rows themselves. if identityBoundary() && !disableIdentPassthrough && (len() == 0 || len([0]) == .RowWidth) { identPassthroughs++ := if len(.Proj.OrderBy) > 0 { = sortRowsByOrder(, &.Proj, .Slots, func(int) []value.Value { return nil }, 0, ) } = paginate(, .Proj.Skip, .Proj.Limit) if != nil { .ProjRows = uint64(len()) } if .PostWhere != nil { // applyPostWhere compacts in place; when out still aliases the // caller's inputs (no sort made a fresh slice), compact a copy. if len(.Proj.OrderBy) == 0 { = slices.Clone() } applyPostWhere(, , &) if != nil { := uint64(len()) .PostWhereRows = & } } return } := segmentBoundSlots() var []value.Value if len() > 0 { = make([]value.Value, .RowWidth) copy(, [0]) } // A slot is batch-constant for hoisting when no stage of the segment // binds it and the seeded inputs agree on its value (a slot beyond an // input's width seeds as Null). := func( int) bool { if < 0 || >= len() || [] { return false } return slotAgrees(, , true) } // uniformIn drops constIn's bound-slot exclusion: a slot every seeded // input agrees on stays uniform through rebinds and arg re-scans // (neither changes a bound value), which is what the chain absorber's // tail needs -- its into-bound target IS "bound" by the tail itself. := func( int) bool { return >= 0 && len() > 0 && slotAgrees(, , true) } var terminal if .Proj.Aggregated { = newAggSink(, &.Proj, .Slots) } else { = newProjSink(, &.Proj, .Slots, .RowWidth) } var rowSink = := make([]stageProfCell, len(.Stages)) // One used-relationship env per chain: a MATCH clause's uniqueness // scope spans its chained stages (comma patterns, planner splits), so // every matchSink of this segment shares the stack; the DFS push/pop // discipline empties it between rows. := &uniqEnv{} for := len(.Stages) - 1; >= 0; -- { var *stageProfCell if != nil { = &[] } = buildStageSink(, , .Stages[], , , , , , ) } := make([]value.Value, .RowWidth) for , := range { clear() copy(, ) if !.push() { break } } .close() := .finalize() if != nil { for , := range { if .match != nil { .Stages = append(.Stages, explain.StageProf{Match: .match}) } else { .Stages = append(.Stages, explain.StageProf{Single: .single}) } } .ProjRows = uint64(len()) } applyPostWhere(, , &) if != nil && .PostWhere != nil { := uint64(len()) .PostWhereRows = & } return } // buildStageSink wires one stage into the chain as a row sink feeding // next, registering its PROFILE counter when cell is non-nil. func buildStageSink( *eval.Ctx, *plan.Segment, plan.Stage, rowSink, , func(int) bool, []value.Value, *stageProfCell, *uniqEnv) rowSink { := func() *uint64 { if == nil { return nil } .single = new(uint64) return .single } switch s := .(type) { case *plan.MatchStage: := &matchSink{ ctx: , stage: , comp: compileStage(, , .Slots, , , , .Proj.Distinct), slots: .Slots, buf: make([]value.Value, .RowWidth), next: , uniq: , } .emitFn = .emit if .Optional { .orig = make([]value.Value, .RowWidth) } if .PathBind != nil && .Where != nil { var []ast.Expr plan.SplitAnd(.Where, &) for , := range { .pathFilters = append(.pathFilters, compileEval(, , .Slots)) } } if != nil { .opRows = make([]uint64, len(.Ops)+1) .match = .opRows } return case *plan.HashJoinStage: return newHashJoinSink(, , , , , ()) case *plan.SpStage: return &spSink{ctx: , sp: , arena: rowArena{width: .RowWidth}, next: , count: ()} case *plan.GateStage: := &gateSink{ ctx: , gs: , slots: .Slots, arena: rowArena{width: .RowWidth}, next: , count: (), where: compileEval(, .Where, .Slots), } for , := range .Derived { .derived = append(.derived, compileEval(, .Expr, .Slots)) } return case *plan.CallStage: := &callSink{ctx: , cs: , buf: make([]value.Value, .RowWidth), next: , count: ()} if , := .G.(graph.Native); { .native = true .g = .Snapshot() if len(.ArgExprs) > 0 { .argEvals = make([]RowEval, len(.ArgExprs)) for , := range .ArgExprs { .argEvals[] = compileEval(, , .Slots) } .slots = .Slots } else { .values, .hits, .prop = callResults(&.Proc, .g) } } return case *plan.UnwindStage: return &unwindSink{ ctx: , list: compileEval(, .List, .Slots), slots: .Slots, out: .OutSlot, buf: make([]value.Value, .RowWidth), next: , count: (), } case *plan.GroupJoinStage: return &groupJoinSink{ ctx: , gj: , buf: make([]value.Value, .RowWidth), next: , count: (), } default: := .(*plan.CallSubqueryStage) return &subquerySink{ ctx: , cs: , buf: make([]value.Value, .RowWidth), seed: make([]value.Value, len(.ImportSlots)), next: , count: (), } } } // segmentBoundSlots marks every slot bound by any stage of the segment; // those can never be hoisting-constant even when the seeded inputs agree. func segmentBoundSlots( *plan.Segment) []bool { := make([]bool, .RowWidth) := func( int) { if >= 0 && < len() { [] = true } } for , := range .Stages { switch s := .(type) { case *plan.MatchStage: for := range .Ops { := &.Ops[] (slotOf()) if .Kind == plan.OpExpand || .Kind == plan.OpVarExpand { (.RelSlot) } } if .PathBind != nil { (.PathBind.PathSlot) } case *plan.HashJoinStage: for , := range .PayloadSlots { () } case *plan.SpStage: (.PathSlot) case *plan.GateStage: (.Sp.PathSlot) for , := range .Derived { (.Slot) } case *plan.CallStage: (.NodeSlot) (.ValueSlot) case *plan.UnwindStage: (.OutSlot) case *plan.CallSubqueryStage: for , := range .OutSlots { () } case *plan.GroupJoinStage: for , := range .OutSlots { () } } } return } // streamableBoundary reports whether seg's projection boundary is pure // per-row passthrough -- no aggregation, DISTINCT, ORDER BY, or // pagination -- so its rows can seed the next segment as they are // produced instead of materializing between segments. func streamableBoundary( *plan.Segment) bool { := &.Proj return !.Aggregated && !.Distinct && len(.OrderBy) == 0 && .Skip == nil && .Limit == nil && len(.Post) == 0 && .NHidden == 0 } // passthroughSink is a streamed projection boundary: it projects the // upstream segment's output columns, applies the boundary WHERE, and // re-seeds the downstream chain's input buffer per row. Only built for // streamableBoundary segments, where this is value-identical to // finalize-then-reseed. type passthroughSink struct { ctx *eval.Ctx returns []RowEval slots map[string]int where RowEval colScope map[string]int out []value.Value buf []value.Value next rowSink } func newPassthroughSink( *eval.Ctx, *plan.Segment, int, rowSink) *passthroughSink { := &passthroughSink{ ctx: , returns: make([]RowEval, len(.Proj.Returns)), slots: .Slots, out: make([]value.Value, len(.Proj.Returns)), buf: make([]value.Value, ), next: , } for , := range .Proj.Returns { .returns[] = compileEval(, .Expr, .Slots) } if .PostWhere != nil { .colScope = make(map[string]int, len(.Proj.Columns)) for , := range .Proj.Columns { .colScope[] = } .where = compileEval(, .PostWhere, .colScope) } return } func ( *passthroughSink) ( []value.Value) bool { for , := range .returns { .out[] = .Eval(.ctx, , .slots) } if .where != nil && !.where.Eval(.ctx, .out, .colScope).IsTruthy() { return true } clear(.buf) copy(.buf, .out) return .next.push(.buf) } func ( *passthroughSink) () { .next.close() } // buildChain wires a segment's stages onto term, returning the head sink // (no PROFILE cells: streaming runs are unprofiled by construction). func buildChain( *eval.Ctx, *plan.Segment, rowSink, , func(int) bool, []value.Value) rowSink { := &uniqEnv{} := for := len(.Stages) - 1; >= 0; -- { = buildStageSink(, , .Stages[], , , , , nil, ) } return } // runSegmentRun executes a run of segments whose interior boundaries are // all streamable: every non-final segment's terminal becomes a // passthrough into the next segment's chain, so rows cross boundaries as // they are produced and only the final terminal retains state. The first // segment keeps the materialized-inputs batch-constant analysis; the // streamed-into segments compile conservatively (nothing batch-constant // -- a perf-only distinction, cInCarried still applies per epoch). func runSegmentRun( *eval.Ctx, []*plan.Segment, [][]value.Value) [][]value.Value { := [len()-1] if len() == 1 { return runSegment(, , , nil) } := func(int) bool { return false } var terminal if .Proj.Aggregated { = newAggSink(, &.Proj, .Slots) } else { = newProjSink(, &.Proj, .Slots, .RowWidth) } := buildChain(, , , , , nil) for := len() - 2; >= 0; -- { := [] := newPassthroughSink(, , [+1].RowWidth, ) := := var []value.Value if == 0 { := segmentBoundSlots() if len() > 0 { = make([]value.Value, .RowWidth) copy(, [0]) } = func( int) bool { if < 0 || >= len() || [] { return false } return slotAgrees(, , true) } = func( int) bool { return >= 0 && len() > 0 && slotAgrees(, , true) } } = buildChain(, , , , , ) } := make([]value.Value, [0].RowWidth) for , := range { clear() copy(, ) if !.push() { break } } .close() := .finalize() applyPostWhere(, , &) return } // applyPostWhere applies a segment's projection-boundary WHERE (FILTER / // RETURN...NEXT guard) to its output rows in place, by output column. func applyPostWhere( *eval.Ctx, *plan.Segment, *[][]value.Value) { if .PostWhere == nil { return } := make(map[string]int, len(.Proj.Columns)) for , := range .Proj.Columns { [] = } // An output column constant across every projected row (e.g. an // ungrouped collect broadcast) lets an IN over it probe a prebuilt set. := * := func( int) bool { return slotAgrees(, , false) } var []value.Value if len() > 0 { = [0] } := hoistEval(, compileEval(, .PostWhere, ), , func(int) bool { return false }, , ) := (*)[:0] for , := range * { if .Eval(, , ).IsTruthy() { = append(, ) } } * = }