// Early shortest-path row gating: when a segment's whole job is one// non-expanding ANY SHORTEST stage and the following boundary chain is a// pure per-row LET/FILTER pipeline, the path search plus every// gate-evaluable filter conjunct also runs INSIDE the previous segment,// inserted before its remaining expansion stages -- so rows the chain// would kill never pay the expansion (the BFS-first order a hand-written// kernel uses). The injection is a semijoin-style copy, not a move: the// downstream stages run unchanged, which keeps the transform sound by// construction -- the gate only removes rows a later deterministic// per-row filter would remove anyway (the path search is a pure function// of its endpoint pair and the graph). Bindings hide past the segment's// projected names, so nothing else can observe them.
package planimport// injectSPGates runs the gating pass over a branch's built segments.func injectSPGates( []*Segment) {for := 0; +1 < len(); ++ {injectSPGate(, ) }}// injectSPGate considers hoisting segment i+1's shortest-path stage (and// the pure filter chain behind it) into segment i as a GateStage. Every// bail-out leaves the plan untouched.func injectSPGate( []*Segment, int) { , := [], [+1]// The gated segment must be exactly one required single-path stage: an // ALL form expands rows and an OPTIONAL one keeps them, so neither can // act as a filter.iflen(.Stages) != 1 {return } , := .Stages[0].(*SpStage)if ! || .All || .Optional {return }// The host's boundary must be a pure per-row projection: filtering // before it commutes only when it neither aggregates, dedups, orders, // paginates, nor post-computes.if !pureRowProj(&.Proj) {return }// Resolve the stage's slots to names through its own segment. := make(map[int]string, len(.Slots))for , := range .Slots { [] = } , , := [.From], [.To], [.PathSlot]if == "" || == "" || == "" {return }// A referenced column is gate-evaluable when the host projects it as // itself (a bare variable under its own name): then the host's slot // holds exactly the value the chain saw. := map[string]bool{}for , := range .Proj.Returns {ifisVar(.Expr, .Name) {if , := .Slots[.Name]; { [.Name] = true } } }if ![] || ![] {return }if , := .Slots[]; {return } [] = true// Walk the boundary chain from the gated segment forward while it // stays a pure per-row pipeline, gathering evaluable LET columns and // filter conjuncts. Conjuncts are copied, never consumed -- the // downstream filter re-running on survivors is idempotent. := func() []string { := make([]string, 0, len())for := range { = append(, ) }return }typestruct {stringast.Expr }var []var []ast.Expr := map[string]bool{: true, : true}for := + 1; < len(); ++ { := []if > +1 && len(.Stages) != 0 {break }if !pureRowProj(&.Proj) {break } := truefor , := range .Proj.Returns {ifisVar(.Expr, .Name) {continue }if , := .Slots[.Name]; || [.Name] { = falsecontinue }iflen(freeVarsOutside(.Expr, ())) != 0 {continue } = append(, {: .Name, : .Expr}) [.Name] = true }if ! {break }if .PostWhere != nil {var []ast.ExprSplitAnd(.PostWhere, &)for , := range {iflen(freeVarsOutside(, ())) == 0 { = append(, ) } } } }iflen() == 0 {return }for , := range {collectAllVars(., ) }for , := range {collectAllVars(, ) }// Insertion point: the earliest stage boundary where every host slot // the gate reads is bound, that does not split a relationship- // uniqueness scope, and that leaves at least one stage after it (a // gate just before the projection saves nothing). := gateInsertPos(, , )if < 0 || >= len(.Stages) {return }// Materialize: hidden slots for the path and each hoisted LET, the // names registered so the copied expressions resolve against the // host's own slot map. := &GateStage{Sp: *, Where: rebuildAnd()} := .RowWidth .Slots[] = .Sp.PathSlot = .Sp.From = .Slots[] .Sp.To = .Slots[] ++for , := range { .Slots[.] = .Derived = append(.Derived, GateDerived{Slot: , Expr: .}) ++ } .RowWidth = .Stages = append(.Stages[:], append([]Stage{}, .Stages[:]...)...)}// pureRowProj reports whether a projection is per-row passthrough shaped:// no aggregation, DISTINCT, ordering, pagination, or post-aggregate// computation. Filtering its input commutes with it.func pureRowProj( *ProjPlan) bool {return !.Aggregated && !.Distinct && len(.OrderBy) == 0 && .Skip == nil && .Limit == nil && len(.Post) == 0 && .NHidden == 0}// gateInsertPos is the earliest stage boundary of a where every slot named// by need (that a's slot map knows and the host can evaluate) is bound --// by the segment's inputs or a preceding stage -- without splitting a// relationship-uniqueness scope across the boundary; -1 when none.func gateInsertPos( *Segment, , map[string]bool) int { := map[int]bool{}for := range {if ![] {continue }if , := .Slots[]; { [] = true } } := make([][]int, len(.Stages)) := map[int]bool{}for , := range .Stages {stageSlotBinds(, func( int) { [] = append([], ) [] = true }) }// Slots the segment's stages never bind are inputs: bound before // stage 0. := map[int]bool{}for , := range .Slots {if ![] { [] = true } } := func() bool {for := range {if ![] {returnfalse } }returntrue }for := 0; <= len(.Stages); ++ {if () && !splitsUniqScope(.Stages, ) {return }if < len(.Stages) {for , := range [] { [] = true } } }return -1}// stageSlotBinds reports every slot a stage binds.func stageSlotBinds( Stage, func(int)) {switch s := .(type) {case *MatchStage:for := range .Ops { := &.Ops[]if .Kind == OpScan { (.Slot) } else { (.To) }if .Kind == OpExpand || .Kind == OpVarExpand { (.RelSlot) } }if .PathBind != nil { (.PathBind.PathSlot) }case *HashJoinStage:for , := range .PayloadSlots { () } (.KeySlot)case *SpStage: (.PathSlot)case *GateStage: (.Sp.PathSlot)for , := range .Derived { (.Slot) }case *CallStage: (.NodeSlot) (.ValueSlot) (.DepthSlot)case *UnwindStage: (.OutSlot)case *CallSubqueryStage:for , := range .OutSlots { () } }}// splitsUniqScope reports whether inserting a batch barrier at pos would// separate two stages sharing a relationship-uniqueness scope: the// barrier buffers rows, so used-relationship pairs pushed by the earlier// stage would already be popped when the later stage checks them.func splitsUniqScope( []Stage, int) bool { := map[uint32]bool{}for , := range [:] {stageUniqScopes(, func( uint32) { [] = true }) } := falsefor , := range [:] {stageUniqScopes(, func( uint32) {if [] { = true } }) }return}// stageUniqScopes reports the relationship-uniqueness scopes a stage// participates in.func stageUniqScopes( Stage, func(uint32)) {switch s := .(type) {case *MatchStage: (.Scope)case *HashJoinStage:for , := range .Build { (.Scope) }if .Probe.Uniq != nil { (.Probe.Uniq.Scope) } }}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.