// Fused columnar aggregation: a chain marked by plan/colagg.go -- one // bare label-scan segment, a run of stage-less LET boundaries, and a // stage-less aggregated boundary -- runs as ONE typed pass over the // scanned label's ids. Filters run through the per-candidate predicate // machinery, group keys and aggregate arguments through typed column // reads resolved across the boundary chain by name; grouping, aggregate // state, post-aggregation wrappers, ordering, and pagination reuse the // general path's exact kernels (aggState, sortRowsByOrder, paginate), so // the fused result is identical by construction. ANY expression the // classifier cannot express over typed columns declines the whole chain // back to the general pipeline. Classification is shape-generic: it // never asks what query a chain came from, only whether its expressions // fit the typed vocabulary.
package exec import ( chickpeas ) // disableColAgg pins differential tests to the general path; // colAggFired counts successful fusions so tests can assert the fused // arm actually took the fused path (a silent double-general run would // pass any differential vacuously). var ( disableColAgg = false colAggFired int ) // colAggMaxKeys is the fused group key width (a fixed-size comparable map // key); wider groupings keep the general path. const colAggMaxKeys = 4 // colAggKeyKind says how a computed key's int64 unboxes for output. type colAggKeyKind uint8 const ( cakInt colAggKeyKind = iota cakBool ) // colKeyFn is a typed per-candidate key: ok=false is a null key (its own // group), mirroring the boxed evaluation's Null. type colKeyFn func(id uint32) (int64, bool) // colEntry is one resolved column name flowing through the chain. type colEntry struct { isV bool fn colKeyFn kind colAggKeyKind carried bool val value.Value // carried entries: the (single input row) value } // colEnv resolves column names during the chain walk. synthSlots/synthRow // let chain expressions compile against carried values and the scanned // candidate (slot 0) through the ordinary compile machinery. type colEnv struct { entries map[string]colEntry synthSlots map[string]int synthRow []value.Value ctx *eval.Ctx g *chickpeas.Snapshot } // tryColumnarAggChain fuses the chain headed at segments[i], reporting // (output rows, segments consumed, true) or ok=false with nothing // consumed. Exactness domain: exactly one input row (with more, carried // group keys would participate in cross-row grouping). func tryColumnarAggChain( *eval.Ctx, []*plan.Segment, int, [][]value.Value) ([][]value.Value, int, bool) { if disableColAgg || len() != 1 { return nil, 0, false } := plan.ColAggChainLen(, ) if == 0 { return nil, 0, false } , := .G.(graph.Native) if ! { return nil, 0, false } := .Snapshot() := [] := .Stages[0].(*plan.MatchStage) := &.Ops[0] := .Slot := "" for , := range .Slots { if == { = } } if == "" { return nil, 0, false } // Head-segment scratch row: the seeded input, for carried references // in filters and carried column evaluation. := make([]value.Value, .RowWidth) copy(, [0]) := func( int) bool { return != && >= 0 && < len() } := func(int) bool { return false } .MatchEpoch++ // Filters: every WHERE conjunct of the scan stage must specialize -- // i64-column-vs-constant conjuncts to bulk IntSweeps (one column // dispatch per chunk), everything else to a per-candidate predicate. var []compile.IntSweep var []compile.CandPred if .Where != nil { var []ast.Expr plan.SplitAnd(.Where, &) for , := range { := compile.HoistCarriedIn(compile.HoistConstIn(, compile.New(, , .Slots, ), , , .Slots), ) if , := compile.CandidateIntSweep(, ); { = append(, ) continue } , := compile.CandidatePred(, , .Slots) if ! { return nil, 0, false } = append(, ) } } // The chain env starts from the head's input columns (carried) and // the scanned var, then each boundary re-maps it through its Returns. := &colEnv{entries: map[string]colEntry{}, synthSlots: map[string]int{}, ctx: , g: } .synthSlots[] = 0 .synthRow = append(.synthRow, value.Value{}) .entries[] = colEntry{isV: true} for , := range .Slots { if == { continue } var value.Value if >= 0 && < len([0]) { = [0][] } .addCarried(, ) } // Boundaries: the head's projection and each interior pass-through // re-map the env; the aggregated boundary itself is consumed by the // key/argument classification below, never re-mapped (its returns // are the aggregate outputs). A single-segment chain applies no // boundary at all -- its keys and arguments resolve over the head's // own inputs. for := ; < +-1; ++ { if !.applyBoundary(&[].Proj) { return nil, 0, false } } := [+-1] := &.Proj // Group keys and aggregate arguments resolve against the final env. var []colEntry := 0 for , := range .GroupIdx { , := .resolveExpr(.Returns[].Expr) if ! { return nil, 0, false } if .isV { return nil, 0, false } = append(, ) if !.carried { ++ } } if > colAggMaxKeys { return nil, 0, false } var []struct { func( uint32) value.Value bool } for , := range .Aggs { switch arg := .Arg.(type) { case nil: = append(, struct { func( uint32) value.Value bool }{: true}) case *ast.Var: , := .entries[.Name] if ! || !.isV { return nil, 0, false } = append(, struct { func( uint32) value.Value bool }{: func( uint32) value.Value { return value.Node(graph.NodeID()) }}) case *ast.Prop: , := .entries[.Var] if ! || !.isV { return nil, 0, false } , := colAggPropValue(, .Key) if ! { return nil, 0, false } = append(, struct { func( uint32) value.Value bool }{: }) default: return nil, 0, false } } // The fused pass: filter, key, accumulate. , := .NodesWithLabel(.Source.Label) type struct { [colAggMaxKeys]int64 uint8 } := map[]int{} var []aggState var [] := func( uint32) { for , := range { if !(, , graph.NodeID()) { return } } var := 0 for , := range { if .carried { continue } , := .fn() if { .[] = } else { . |= 1 << } ++ } // Small aggregates (the common colagg shape) probe the key list // linearly -- comparing a few comparable structs beats hashing a // 32-byte key per row; past the threshold the map takes over, // seeded with the linear era's entries. const = 16 , := -1, false if len() <= { for := range { if [] == { , = , true break } } } else { , = [] } if ! { = len() = append(, ) if len() == +1 && len() == 0 { for := range { [[]] = } } else if len() > { [] = } for , := range .Aggs { = append(, aggState{kind: .Kind}) } } := [*len(.Aggs) : (+1)*len(.Aggs)] for , := range { if . { [].update(value.Value{}, false) } else { [].update(.(), true) } } } if { // Chunked candidate walk: sweeps bulk-filter each id chunk (one // column dispatch per conjunct per chunk), then survivors run // the per-candidate predicates and accumulate. const = 4096 := make([]int64, ) := make([]bool, ) := make([]bool, ) := func( []uint32) { for := range { [] = true } for , := range { (, [:len()], [:len()], [:len()]) } for , := range { if [] { () } } } // A selective range conjunct flips the enumeration: instead of // every labeled node testing every predicate, walk the range // index's window (exact count, no estimate) and test label // membership -- chosen only when the window is decisively smaller // than the label, so broad ranges keep the cache-linear label // sweep. Every predicate still runs per candidate (the window is // a superset reduction), so results are identical either way. if := colAggRangeWindow(, .Where, , ); != nil && len() < .Len()/4 { := .LabelDense(.Source.Label) := make([]uint32, 0, ) := func() { if len() > 0 { () = [:0] } } for , := range { := false if != nil { := int() >> 6 = < len() && []>>(&63)&1 == 1 } else { = .Contains() } if { if = append(, ); len() == { () } } } () } else { := make([]uint32, ) .IterChunks(, func( []uint32) bool { () return true }) } } // No candidate survived. A keyless aggregate over no input still // emits one zeroed group, exactly like the general accumulator. An // OPTIONAL scan instead emits its null-fill row THROUGH the // aggregator: star aggregates count that row (count(*) reads 1), // value aggregates read null from it (their identity), computed // group keys take the null key, carried keys keep their carried // values. Established against the general path's own output -- // grouped plain MATCH stays zero rows, everything else fills. if len() == 0 && (len(.GroupIdx) == 0 || .Optional) { := {} if .Optional { for := 0; < ; ++ { . |= 1 << } } = append(, ) for , := range .Aggs { = append(, aggState{kind: .Kind}) } if .Optional { for , := range { if . { [].update(value.Value{}, false) } else { [].update(value.Null(), true) } } } } // Output assembly mirrors aggregator.finalize: keys and aggregate // finals into a hidden-slot-extended row, post wrappers, truncate, // order, paginate. := len(.Returns) := make(map[string]int, .NHidden+len(.GroupIdx)) for := 0; < .NHidden; ++ { [hiddenAggName()] = + } for , := range .GroupIdx { if , := [.Returns[].Name]; ! { [.Returns[].Name] = } } := make([]RowEval, len(.Post)) for , := range .Post { [] = compileEval(, .Expr, ) } := make([][]value.Value, 0, len()) for , := range { := make([]value.Value, +.NHidden) := 0 for , := range .GroupIdx { := [] if .carried { [] = .val continue } switch { case .&(1<<) != 0: [] = value.Null() case .kind == cakBool: [] = value.Bool(.[] != 0) default: [] = value.Int(.[]) } ++ } := [*len(.Aggs) : (+1)*len(.Aggs)] for := range .Aggs { [.Aggs[].OutIdx] = [].finalize() } for , := range .Post { [.Col] = [].Eval(, , ) } = append(, [:]) } if len(.OrderBy) > 0 { = sortRowsByOrder(, , .Slots, func(int) []value.Value { return nil }, 0, ) } = paginate(, .Skip, .Limit) if .PostWhere != nil { applyPostWhere(, , &) } colAggFired++ return , , true }