github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/min_max_agg_tmpl.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // {{/* 12 // +build execgen_template 13 // 14 // This file is the execgen template for min_max_agg.eg.go. It's formatted in a 15 // special way, so it's both valid Go and a valid text/template input. This 16 // permits editing this file with editor support. 17 // 18 // */}} 19 20 package colexec 21 22 import ( 23 "unsafe" 24 25 "github.com/cockroachdb/cockroach/pkg/col/coldata" 26 "github.com/cockroachdb/cockroach/pkg/col/typeconv" 27 "github.com/cockroachdb/cockroach/pkg/sql/colexec/execgen" 28 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror" 29 "github.com/cockroachdb/cockroach/pkg/sql/colmem" 30 "github.com/cockroachdb/cockroach/pkg/sql/types" 31 "github.com/cockroachdb/errors" 32 ) 33 34 // Remove unused warning. 35 var _ = execgen.UNSAFEGET 36 37 // Remove unused warning. 38 var _ = colexecerror.InternalError 39 40 // {{/* 41 // Declarations to make the template compile properly. 42 43 // _GOTYPESLICE is the template variable. 44 type _GOTYPESLICE interface{} 45 46 // _CANONICAL_TYPE_FAMILY is the template variable. 47 const _CANONICAL_TYPE_FAMILY = types.UnknownFamily 48 49 // _TYPE_WIDTH is the template variable. 50 const _TYPE_WIDTH = 0 51 52 // _ASSIGN_CMP is the template function for assigning true to the first input 53 // if the second input compares successfully to the third input. The comparison 54 // operator is tree.LT for MIN and is tree.GT for MAX. 55 func _ASSIGN_CMP(_, _, _, _, _, _ string) bool { 56 colexecerror.InternalError("") 57 } 58 59 // */}} 60 61 // {{range .}} {{/* for each aggregation (min and max) */}} 62 63 // {{/* Capture the aggregation name so we can use it in the inner loop. */}} 64 // {{$agg := .AggNameLower}} 65 66 func new_AGG_TITLEAggAlloc( 67 allocator *colmem.Allocator, t *types.T, allocSize int64, 68 ) (aggregateFuncAlloc, error) { 69 switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { 70 // {{range .Overloads}} 71 case _CANONICAL_TYPE_FAMILY: 72 switch t.Width() { 73 // {{range .WidthOverloads}} 74 case _TYPE_WIDTH: 75 return &_AGG_TYPEAggAlloc{allocator: allocator, allocSize: allocSize}, nil 76 // {{end}} 77 } 78 // {{end}} 79 } 80 return nil, errors.Errorf("unsupported _AGG agg type %s", t.Name()) 81 } 82 83 // {{range .Overloads}} 84 // {{range .WidthOverloads}} 85 86 type _AGG_TYPEAgg struct { 87 allocator *colmem.Allocator 88 groups []bool 89 curIdx int 90 // curAgg holds the running min/max, so we can index into the slice once per 91 // group, instead of on each iteration. 92 // NOTE: if foundNonNullForCurrentGroup is false, curAgg is undefined. 93 curAgg _GOTYPE 94 // col points to the output vector we are updating. 95 col _GOTYPESLICE 96 // vec is the same as col before conversion from coldata.Vec. 97 vec coldata.Vec 98 // nulls points to the output null vector that we are updating. 99 nulls *coldata.Nulls 100 // foundNonNullForCurrentGroup tracks if we have seen any non-null values 101 // for the group that is currently being aggregated. 102 foundNonNullForCurrentGroup bool 103 } 104 105 var _ aggregateFunc = &_AGG_TYPEAgg{} 106 107 const sizeOf_AGG_TYPEAgg = int64(unsafe.Sizeof(_AGG_TYPEAgg{})) 108 109 func (a *_AGG_TYPEAgg) Init(groups []bool, v coldata.Vec) { 110 a.groups = groups 111 a.vec = v 112 a.col = v._TYPE() 113 a.nulls = v.Nulls() 114 a.Reset() 115 } 116 117 func (a *_AGG_TYPEAgg) Reset() { 118 a.curIdx = -1 119 a.foundNonNullForCurrentGroup = false 120 a.nulls.UnsetNulls() 121 } 122 123 func (a *_AGG_TYPEAgg) CurrentOutputIndex() int { 124 return a.curIdx 125 } 126 127 func (a *_AGG_TYPEAgg) SetOutputIndex(idx int) { 128 if a.curIdx != -1 { 129 a.curIdx = idx 130 a.nulls.UnsetNullsAfter(idx + 1) 131 } 132 } 133 134 func (a *_AGG_TYPEAgg) Compute(b coldata.Batch, inputIdxs []uint32) { 135 inputLen := b.Length() 136 vec, sel := b.ColVec(int(inputIdxs[0])), b.Selection() 137 col, nulls := vec._TYPE(), vec.Nulls() 138 a.allocator.PerformOperation( 139 []coldata.Vec{a.vec}, 140 func() { 141 if nulls.MaybeHasNulls() { 142 if sel != nil { 143 sel = sel[:inputLen] 144 for _, i := range sel { 145 _ACCUMULATE_MINMAX(a, nulls, i, true) 146 } 147 } else { 148 col = execgen.SLICE(col, 0, inputLen) 149 for execgen.RANGE(i, col, 0, inputLen) { 150 _ACCUMULATE_MINMAX(a, nulls, i, true) 151 } 152 } 153 } else { 154 if sel != nil { 155 sel = sel[:inputLen] 156 for _, i := range sel { 157 _ACCUMULATE_MINMAX(a, nulls, i, false) 158 } 159 } else { 160 col = execgen.SLICE(col, 0, inputLen) 161 for execgen.RANGE(i, col, 0, inputLen) { 162 _ACCUMULATE_MINMAX(a, nulls, i, false) 163 } 164 } 165 } 166 }, 167 ) 168 } 169 170 func (a *_AGG_TYPEAgg) Flush() { 171 // The aggregation is finished. Flush the last value. If we haven't found 172 // any non-nulls for this group so far, the output for this group should 173 // be null. 174 if !a.foundNonNullForCurrentGroup { 175 a.nulls.SetNull(a.curIdx) 176 } else { 177 execgen.SET(a.col, a.curIdx, a.curAgg) 178 } 179 a.curIdx++ 180 } 181 182 func (a *_AGG_TYPEAgg) HandleEmptyInputScalar() { 183 a.nulls.SetNull(0) 184 } 185 186 type _AGG_TYPEAggAlloc struct { 187 allocator *colmem.Allocator 188 allocSize int64 189 aggFuncs []_AGG_TYPEAgg 190 } 191 192 var _ aggregateFuncAlloc = &_AGG_TYPEAggAlloc{} 193 194 func (a *_AGG_TYPEAggAlloc) newAggFunc() aggregateFunc { 195 if len(a.aggFuncs) == 0 { 196 a.allocator.AdjustMemoryUsage(sizeOf_AGG_TYPEAgg * a.allocSize) 197 a.aggFuncs = make([]_AGG_TYPEAgg, a.allocSize) 198 } 199 f := &a.aggFuncs[0] 200 f.allocator = a.allocator 201 a.aggFuncs = a.aggFuncs[1:] 202 return f 203 } 204 205 // {{end}} 206 // {{end}} 207 // {{end}} 208 209 // {{/* 210 // _ACCUMULATE_MINMAX sets the output for the current group to be the value of 211 // the ith row if it is smaller/larger than the current result. If this is the 212 // first row of a new group, and no non-nulls have been found for the current 213 // group, then the output for the current group is set to null. 214 func _ACCUMULATE_MINMAX(a *_AGG_TYPEAgg, nulls *coldata.Nulls, i int, _HAS_NULLS bool) { // */}} 215 216 // {{define "accumulateMinMax"}} 217 if a.groups[i] { 218 // If we encounter a new group, and we haven't found any non-nulls for the 219 // current group, the output for this group should be null. If a.curIdx is 220 // negative, it means that this is the first group. 221 if a.curIdx >= 0 { 222 if !a.foundNonNullForCurrentGroup { 223 a.nulls.SetNull(a.curIdx) 224 } else { 225 // {{with .Global}} 226 execgen.SET(a.col, a.curIdx, a.curAgg) 227 // {{end}} 228 } 229 } 230 a.curIdx++ 231 a.foundNonNullForCurrentGroup = false 232 } 233 var isNull bool 234 // {{if .HasNulls}} 235 isNull = nulls.NullAt(i) 236 // {{else}} 237 isNull = false 238 // {{end}} 239 // {{with .Global}} 240 if !isNull { 241 if !a.foundNonNullForCurrentGroup { 242 val := execgen.UNSAFEGET(col, i) 243 execgen.COPYVAL(a.curAgg, val) 244 a.foundNonNullForCurrentGroup = true 245 } else { 246 var cmp bool 247 candidate := execgen.UNSAFEGET(col, i) 248 _ASSIGN_CMP(cmp, candidate, a.curAgg, _, col, _) 249 if cmp { 250 execgen.COPYVAL(a.curAgg, candidate) 251 } 252 } 253 } 254 // {{end}} 255 // {{end}} 256 257 // {{/* 258 } // */}}