github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/builtins/window_frame_builtins_test.go (about) 1 // Copyright 2018 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 package builtins 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "math/rand" 18 "testing" 19 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 ) 24 25 const maxInt = 1000000 26 const maxOffset = 100 27 28 type indexedRows struct { 29 rows []indexedRow 30 } 31 32 func (ir indexedRows) Len() int { 33 return len(ir.rows) 34 } 35 36 func (ir indexedRows) GetRow(_ context.Context, idx int) (tree.IndexedRow, error) { 37 return ir.rows[idx], nil 38 } 39 40 type indexedRow struct { 41 idx int 42 row tree.Datums 43 } 44 45 func (ir indexedRow) GetIdx() int { 46 return ir.idx 47 } 48 49 func (ir indexedRow) GetDatum(colIdx int) (tree.Datum, error) { 50 return ir.row[colIdx], nil 51 } 52 53 func (ir indexedRow) GetDatums(firstColIdx, lastColIdx int) (tree.Datums, error) { 54 return ir.row[firstColIdx:lastColIdx], nil 55 } 56 57 func testSlidingWindow(t *testing.T, count int) { 58 evalCtx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 59 defer evalCtx.Stop(context.Background()) 60 wfr := makeTestWindowFrameRun(count) 61 wfr.Frame = &tree.WindowFrame{ 62 Mode: tree.ROWS, 63 Bounds: tree.WindowFrameBounds{ 64 StartBound: &tree.WindowFrameBound{BoundType: tree.OffsetPreceding}, 65 EndBound: &tree.WindowFrameBound{BoundType: tree.OffsetFollowing}, 66 }, 67 } 68 testMin(t, evalCtx, wfr) 69 testMax(t, evalCtx, wfr) 70 testSumAndAvg(t, evalCtx, wfr) 71 } 72 73 func testMin(t *testing.T, evalCtx *tree.EvalContext, wfr *tree.WindowFrameRun) { 74 for offset := 0; offset < maxOffset; offset += int(rand.Int31n(maxOffset / 10)) { 75 wfr.StartBoundOffset = tree.NewDInt(tree.DInt(offset)) 76 wfr.EndBoundOffset = tree.NewDInt(tree.DInt(offset)) 77 min := &slidingWindowFunc{} 78 min.sw = makeSlidingWindow(evalCtx, func(evalCtx *tree.EvalContext, a, b tree.Datum) int { 79 return -a.Compare(evalCtx, b) 80 }) 81 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 82 res, err := min.Compute(evalCtx.Ctx(), evalCtx, wfr) 83 if err != nil { 84 t.Errorf("Unexpected error received when getting min from sliding window: %+v", err) 85 } 86 minResult, _ := tree.AsDInt(res) 87 naiveMin := tree.DInt(maxInt) 88 for idx := wfr.RowIdx - offset; idx <= wfr.RowIdx+offset; idx++ { 89 if idx < 0 || idx >= wfr.PartitionSize() { 90 continue 91 } 92 row, err := wfr.Rows.GetRow(evalCtx.Ctx(), idx) 93 if err != nil { 94 panic(err) 95 } 96 datum, err := row.GetDatum(0) 97 if err != nil { 98 panic(err) 99 } 100 el, _ := tree.AsDInt(datum) 101 if el < naiveMin { 102 naiveMin = el 103 } 104 } 105 if minResult != naiveMin { 106 t.Errorf("Min sliding window returned wrong result: expected %+v, found %+v", naiveMin, minResult) 107 t.Errorf("partitionSize: %+v idx: %+v offset: %+v", wfr.PartitionSize(), wfr.RowIdx, offset) 108 t.Errorf(min.sw.string()) 109 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 110 panic("") 111 } 112 } 113 } 114 } 115 116 func testMax(t *testing.T, evalCtx *tree.EvalContext, wfr *tree.WindowFrameRun) { 117 for offset := 0; offset < maxOffset; offset += int(rand.Int31n(maxOffset / 10)) { 118 wfr.StartBoundOffset = tree.NewDInt(tree.DInt(offset)) 119 wfr.EndBoundOffset = tree.NewDInt(tree.DInt(offset)) 120 max := &slidingWindowFunc{} 121 max.sw = makeSlidingWindow(evalCtx, func(evalCtx *tree.EvalContext, a, b tree.Datum) int { 122 return a.Compare(evalCtx, b) 123 }) 124 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 125 res, err := max.Compute(evalCtx.Ctx(), evalCtx, wfr) 126 if err != nil { 127 t.Errorf("Unexpected error received when getting max from sliding window: %+v", err) 128 } 129 maxResult, _ := tree.AsDInt(res) 130 naiveMax := tree.DInt(-maxInt) 131 for idx := wfr.RowIdx - offset; idx <= wfr.RowIdx+offset; idx++ { 132 if idx < 0 || idx >= wfr.PartitionSize() { 133 continue 134 } 135 row, err := wfr.Rows.GetRow(evalCtx.Ctx(), idx) 136 if err != nil { 137 panic(err) 138 } 139 datum, err := row.GetDatum(0) 140 if err != nil { 141 panic(err) 142 } 143 el, _ := tree.AsDInt(datum) 144 if el > naiveMax { 145 naiveMax = el 146 } 147 } 148 if maxResult != naiveMax { 149 t.Errorf("Max sliding window returned wrong result: expected %+v, found %+v", naiveMax, maxResult) 150 t.Errorf("partitionSize: %+v idx: %+v offset: %+v", wfr.PartitionSize(), wfr.RowIdx, offset) 151 t.Errorf(max.sw.string()) 152 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 153 panic("") 154 } 155 } 156 } 157 } 158 159 func testSumAndAvg(t *testing.T, evalCtx *tree.EvalContext, wfr *tree.WindowFrameRun) { 160 for offset := 0; offset < maxOffset; offset += int(rand.Int31n(maxOffset / 10)) { 161 wfr.StartBoundOffset = tree.NewDInt(tree.DInt(offset)) 162 wfr.EndBoundOffset = tree.NewDInt(tree.DInt(offset)) 163 sum := &slidingWindowSumFunc{agg: &intSumAggregate{}} 164 avg := &avgWindowFunc{sum: newSlidingWindowSumFunc(&intSumAggregate{})} 165 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 166 res, err := sum.Compute(evalCtx.Ctx(), evalCtx, wfr) 167 if err != nil { 168 t.Errorf("Unexpected error received when getting sum from sliding window: %+v", err) 169 } 170 sumResult := tree.DDecimal{Decimal: res.(*tree.DDecimal).Decimal} 171 res, err = avg.Compute(evalCtx.Ctx(), evalCtx, wfr) 172 if err != nil { 173 t.Errorf("Unexpected error received when getting avg from sliding window: %+v", err) 174 } 175 avgResult := tree.DDecimal{Decimal: res.(*tree.DDecimal).Decimal} 176 naiveSum := int64(0) 177 for idx := wfr.RowIdx - offset; idx <= wfr.RowIdx+offset; idx++ { 178 if idx < 0 || idx >= wfr.PartitionSize() { 179 continue 180 } 181 row, err := wfr.Rows.GetRow(evalCtx.Ctx(), idx) 182 if err != nil { 183 panic(err) 184 } 185 datum, err := row.GetDatum(0) 186 if err != nil { 187 panic(err) 188 } 189 el, _ := tree.AsDInt(datum) 190 naiveSum += int64(el) 191 } 192 s, err := sumResult.Int64() 193 if err != nil { 194 t.Errorf("Unexpected error received when converting sum from DDecimal to int64: %+v", err) 195 } 196 if s != naiveSum { 197 t.Errorf("Sum sliding window returned wrong result: expected %+v, found %+v", naiveSum, s) 198 t.Errorf("partitionSize: %+v idx: %+v offset: %+v", wfr.PartitionSize(), wfr.RowIdx, offset) 199 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 200 panic("") 201 } 202 a, err := avgResult.Float64() 203 if err != nil { 204 t.Errorf("Unexpected error received when converting avg from DDecimal to float64: %+v", err) 205 } 206 frameSize, err := wfr.FrameSize(evalCtx.Ctx(), evalCtx) 207 if err != nil { 208 t.Errorf("Unexpected error when getting FrameSize: %+v", err) 209 } 210 if a != float64(naiveSum)/float64(frameSize) { 211 t.Errorf("Sum sliding window returned wrong result: expected %+v, found %+v", float64(naiveSum)/float64(frameSize), a) 212 t.Errorf("partitionSize: %+v idx: %+v offset: %+v", wfr.PartitionSize(), wfr.RowIdx, offset) 213 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 214 panic("") 215 } 216 } 217 } 218 } 219 220 const noFilterIdx = -1 221 222 func makeTestWindowFrameRun(count int) *tree.WindowFrameRun { 223 return &tree.WindowFrameRun{ 224 Rows: makeTestPartition(count), 225 ArgsIdxs: []uint32{0}, 226 FilterColIdx: noFilterIdx, 227 } 228 } 229 230 func makeTestPartition(count int) tree.IndexedRows { 231 partition := indexedRows{rows: make([]indexedRow, count)} 232 for idx := 0; idx < count; idx++ { 233 partition.rows[idx] = indexedRow{idx: idx, row: tree.Datums{tree.NewDInt(tree.DInt(rand.Int31n(maxInt)))}} 234 } 235 return partition 236 } 237 238 func partitionToString(ctx context.Context, partition tree.IndexedRows) string { 239 var buf bytes.Buffer 240 var err error 241 var row tree.IndexedRow 242 buf.WriteString("\n=====Partition=====\n") 243 for idx := 0; idx < partition.Len(); idx++ { 244 if row, err = partition.GetRow(ctx, idx); err != nil { 245 return err.Error() 246 } 247 buf.WriteString(fmt.Sprintf("%v\n", row)) 248 } 249 buf.WriteString("====================\n") 250 return buf.String() 251 } 252 253 func TestSlidingWindow(t *testing.T) { 254 defer leaktest.AfterTest(t)() 255 for _, count := range []int{1, 17, 253} { 256 testSlidingWindow(t, count) 257 } 258 }