// The columnar-aggregate chain environment: resolving each per-row // boundary projection against the scanned candidate, classifying an // expression into a typed per-candidate key function (property read, // temporal component, label test, searched CASE), and the range-window // optimization that narrows the scan to an indexed key's window. Split // from colagg.go, which holds the fusion driver (tryColumnarAggChain).
package exec import ( chickpeas ) // addCarried registers a carried (per-input-row constant) column value. func ( *colEnv) ( string, value.Value) { .entries[] = colEntry{carried: true, val: } if , := .synthSlots[]; ! { .synthSlots[] = len(.synthRow) .synthRow = append(.synthRow, ) } else { .synthRow[.synthSlots[]] = } } // applyBoundary re-maps the env through one pure per-row projection: // every output column must resolve, or the chain declines. func ( *colEnv) ( *plan.ProjPlan) bool { := map[string]colEntry{} for , := range .Returns { , := .resolveExpr(.Expr) if ! { return false } [.Name] = } // Rebuild name spaces: keep synth slots for carried survivors and the // scanned var's aliases. := .entries .entries = for , := range { switch { case .isV: .synthSlots[] = 0 case .carried: if , := .synthSlots[]; ! { .synthSlots[] = len(.synthRow) .synthRow = append(.synthRow, .val) } else { .synthRow[.synthSlots[]] = .val } } } _ = return true } // resolveExpr classifies one chain expression against the env: the // scanned var itself, a carried value (evaluated now -- one input row), // or a typed per-candidate key function. func ( *colEnv) ( ast.Expr) (colEntry, bool) { if , := .(*ast.Var); { , := .entries[.Name] return , } if , , := .classifyKeyFn(); { return colEntry{fn: , kind: }, true } // No candidate dependency anywhere: a carried expression, evaluated // against the synthetic row. if .mentionsCandidate() { return colEntry{}, false } := compileEval(.ctx, , .synthSlots) return colEntry{carried: true, val: .Eval(.ctx, .synthRow, .synthSlots)}, true } // mentionsCandidate reports whether e references the scanned var (under // any alias) or a computed key column. func ( *colEnv) ( ast.Expr) bool { for , := range .entries { if (.isV || .fn != nil) && plan.MentionsVar(, ) { return true } } return false } // classifyKeyFn compiles an expression into a typed per-candidate key // function: a computed column reference, an i64/bool property read, a // temporal component over an i64 property, a single-label membership // test, or a searched CASE whose conditions specialize to candidate // predicates and whose results are integer literals. func ( *colEnv) ( ast.Expr) (colKeyFn, colAggKeyKind, bool) { switch n := .(type) { case *ast.Var: , := .entries[.Name] if ! || .fn == nil { return nil, 0, false } return .fn, .kind, true case *ast.Prop: , := .entries[.Var] if ! || !.isV { return nil, 0, false } , := .g.ColIndexed(.Key) if ! { return func(uint32) (int64, bool) { return 0, false }, cakInt, true } switch .Dtype() { case chickpeas.DtypeI64: := .I64() return .Get, cakInt, true case chickpeas.DtypeBool: := .Bool() return func( uint32) (int64, bool) { , := .Get() if ! { return 0, false } if { return 1, true } return 0, true }, cakBool, true } return nil, 0, false case *ast.PropOf: , := .Base.(*ast.Prop) if ! { return nil, 0, false } , := .entries[.Var] if ! || !.isV { return nil, 0, false } , := .g.ColIndexed(.Key) if ! || .Dtype() != chickpeas.DtypeI64 { return nil, 0, false } := .I64() := .Key return func( uint32) (int64, bool) { , := .Get() if ! { return 0, false } return eval.Component(, ) }, cakInt, true case *ast.HasLabelExpr: , := .entries[.Var] if ! || !.isV || .Expr == nil || .Expr.Kind != ast.LabelName { return nil, 0, false } , := .g.NodesWithLabel(.Expr.Name) := .g.LabelDense(.Expr.Name) return func( uint32) (int64, bool) { := false switch { case != nil: := int() >> 6 = < len() && []>>(&63)&1 == 1 case : = .Contains() } if { return 1, true } return 0, true }, cakBool, true case *ast.Case: if .Operand != nil { return nil, 0, false } type struct { compile.CandPred int64 } := make([], 0, len(.Whens)) := func(int) bool { return false } := func( int) bool { return != 0 && < len(.synthRow) } for , := range .Whens { := compile.HoistCarriedIn(compile.HoistConstIn(.ctx, compile.New(.ctx, .Cond, .synthSlots, .g), , .synthRow, .synthSlots), ) , := compile.CandidatePred(, 0, .synthSlots) if ! { return nil, 0, false } , := intLit(.Result) if ! { return nil, 0, false } = append(, {: , : }) } var int64 := false if .Else != nil { var bool if , = intLit(.Else); ! { return nil, 0, false } = true } , := .ctx, .synthRow return func( uint32) (int64, bool) { for , := range { if .(, , graph.NodeID()) { return ., true } } if { return , true } return 0, false }, cakInt, true } return nil, 0, false } // colAggRangeWindow finds the tightest single-key range window implied by // the scan WHERE's comparison conjuncts over the scanned var: every // `v.key CMP const` with an ordered operator and an int/temporal constant // narrows the window for its key, and the key with the smallest window // wins. nil when no conjunct ranges over an indexed i64 column. The // window is a superset of the filtered candidates (the predicates all // re-run), so this only ever changes enumeration order and cost. func colAggRangeWindow( *colEnv, ast.Expr, string, *chickpeas.Snapshot) []uint32 { if == nil { return nil } type struct { , int64 , bool } := map[string]*{} var []ast.Expr plan.SplitAnd(, &) for , := range { , := .(*ast.Binary) if ! { continue } , := .LHS.(*ast.Prop) := .RHS := .Op if ! { if , = .RHS.(*ast.Prop); ! { continue } = .LHS = flipCmp() } if .Var != || .mentionsCandidate() { continue } := compileEval(.ctx, , .synthSlots).Eval(.ctx, .synthRow, .synthSlots) var int64 if , := .AsInt(); { = } else if , , := .AsTemporal(); { = } else { continue } := [.Key] if == nil { = &{: math.MinInt64, : math.MaxInt64, : true, : true} [.Key] = } switch { case ast.OpLt: if <= . { ., . = , false } case ast.OpLte: if < . { ., . = , true } case ast.OpGt: if >= . { ., . = , false } case ast.OpGte: if > . { ., . = , true } case ast.OpEq: if > . { ., . = , true } if < . { ., . = , true } } } var []uint32 := false for , := range { if . == math.MinInt64 && . && . == math.MaxInt64 && . { continue } , := .ColRangeIndex() if ! { continue } := .Window(., ., ., .) if ! || len() < len() { , = , true } } if ! { return nil } return } // flipCmp mirrors a comparison across swapped operands. func flipCmp( ast.BinOp) ast.BinOp { switch { case ast.OpLt: return ast.OpGt case ast.OpLte: return ast.OpGte case ast.OpGt: return ast.OpLt case ast.OpGte: return ast.OpLte } return } // colAggPropValue is a boxed typed read of the scanned var's property // column for aggregate arguments, absent folding to Null exactly like the // compiled property reader. func colAggPropValue( *chickpeas.Snapshot, string) (func( uint32) value.Value, bool) { , := .ColIndexed() if ! { return func(uint32) value.Value { return value.Null() }, true } switch .Dtype() { case chickpeas.DtypeI64: := .I64() return func( uint32) value.Value { if , := .Get(); { return value.Int() } return value.Null() }, true case chickpeas.DtypeF64: := .F64() return func( uint32) value.Value { if , := .Get(); { return value.Float() } return value.Null() }, true } return nil, false } // intLit unwraps an integer literal expression. func intLit( ast.Expr) (int64, bool) { , := .(*ast.Lit) if ! || .Value.Kind != ast.LitInt { return 0, false } return .Value.I, true }