github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/window_funcs_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 tree 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "math/rand" 18 "testing" 19 20 "github.com/cockroachdb/apd" 21 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 24 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 25 ) 26 27 const minOffset = 0 28 const maxOffset = 100 29 const probabilityOfNewNumber = 0.5 30 31 func testRangeMode(t *testing.T, count int) { 32 evalCtx := NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 33 defer evalCtx.Stop(context.Background()) 34 35 wfr := &WindowFrameRun{ 36 Rows: makeIntSortedPartition(count), 37 ArgsIdxs: []uint32{0}, 38 } 39 wfr.PlusOp, wfr.MinusOp, _ = WindowFrameRangeOps{}.LookupImpl(types.Int, types.Int) 40 testStartPreceding(t, evalCtx, wfr, types.Int) 41 testStartFollowing(t, evalCtx, wfr, types.Int) 42 testEndPreceding(t, evalCtx, wfr, types.Int) 43 testEndFollowing(t, evalCtx, wfr, types.Int) 44 45 wfr.Rows = makeFloatSortedPartition(count) 46 wfr.PlusOp, wfr.MinusOp, _ = WindowFrameRangeOps{}.LookupImpl(types.Float, types.Float) 47 testStartPreceding(t, evalCtx, wfr, types.Float) 48 testStartFollowing(t, evalCtx, wfr, types.Float) 49 testEndPreceding(t, evalCtx, wfr, types.Float) 50 testEndFollowing(t, evalCtx, wfr, types.Float) 51 52 wfr.Rows = makeDecimalSortedPartition(count) 53 wfr.PlusOp, wfr.MinusOp, _ = WindowFrameRangeOps{}.LookupImpl(types.Decimal, types.Decimal) 54 testStartPreceding(t, evalCtx, wfr, types.Decimal) 55 testStartFollowing(t, evalCtx, wfr, types.Decimal) 56 testEndPreceding(t, evalCtx, wfr, types.Decimal) 57 testEndFollowing(t, evalCtx, wfr, types.Decimal) 58 } 59 60 func testStartPreceding( 61 t *testing.T, evalCtx *EvalContext, wfr *WindowFrameRun, offsetType *types.T, 62 ) { 63 wfr.Frame = &WindowFrame{ 64 Mode: RANGE, 65 Bounds: WindowFrameBounds{StartBound: &WindowFrameBound{BoundType: OffsetPreceding}}, 66 } 67 for offset := minOffset; offset < maxOffset; offset += rand.Intn(maxOffset / 10) { 68 var typedOffset Datum 69 switch offsetType.Family() { 70 case types.IntFamily: 71 typedOffset = NewDInt(DInt(offset)) 72 case types.FloatFamily: 73 typedOffset = NewDFloat(DFloat(offset)) 74 case types.DecimalFamily: 75 decimal := apd.Decimal{} 76 decimal.SetInt64(int64(offset)) 77 typedOffset = &DDecimal{Decimal: decimal} 78 default: 79 panic("unsupported offset type") 80 } 81 wfr.StartBoundOffset = typedOffset 82 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 83 frameStartIdx, err := wfr.FrameStartIdx(evalCtx.Ctx(), evalCtx) 84 if err != nil { 85 t.Errorf("unexpected error: %v", err) 86 } 87 value, err := wfr.getValueByOffset(evalCtx.Ctx(), evalCtx, typedOffset, true /* negative */) 88 if err != nil { 89 t.Errorf("unexpected error: %v", err) 90 } 91 for idx := 0; idx <= wfr.RowIdx; idx++ { 92 valueAt, err := wfr.valueAt(evalCtx.Ctx(), idx) 93 if err != nil { 94 t.Errorf("unexpected error: %v", err) 95 } 96 if value.Compare(evalCtx, valueAt) <= 0 { 97 if idx != frameStartIdx { 98 t.Errorf("FrameStartIdx returned wrong result on Preceding: expected %+v, found %+v", idx, frameStartIdx) 99 t.Errorf("Search for %+v when wfr.RowIdx=%+v", value, wfr.RowIdx) 100 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 101 panic("") 102 } 103 break 104 } 105 } 106 } 107 } 108 } 109 110 func testStartFollowing( 111 t *testing.T, evalCtx *EvalContext, wfr *WindowFrameRun, offsetType *types.T, 112 ) { 113 wfr.Frame = &WindowFrame{ 114 Mode: RANGE, 115 Bounds: WindowFrameBounds{StartBound: &WindowFrameBound{BoundType: OffsetFollowing}, EndBound: &WindowFrameBound{BoundType: OffsetFollowing}}, 116 } 117 for offset := minOffset; offset < maxOffset; offset += rand.Intn(maxOffset / 10) { 118 var typedOffset Datum 119 switch offsetType.Family() { 120 case types.IntFamily: 121 typedOffset = NewDInt(DInt(offset)) 122 case types.FloatFamily: 123 typedOffset = NewDFloat(DFloat(offset)) 124 case types.DecimalFamily: 125 decimal := apd.Decimal{} 126 decimal.SetInt64(int64(offset)) 127 typedOffset = &DDecimal{Decimal: decimal} 128 default: 129 panic("unsupported offset type") 130 } 131 wfr.StartBoundOffset = typedOffset 132 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 133 frameStartIdx, err := wfr.FrameStartIdx(evalCtx.Ctx(), evalCtx) 134 if err != nil { 135 t.Errorf("unexpected error: %v", err) 136 } 137 value, err := wfr.getValueByOffset(evalCtx.Ctx(), evalCtx, typedOffset, false /* negative */) 138 if err != nil { 139 t.Errorf("unexpected error: %v", err) 140 } 141 for idx := 0; idx <= wfr.PartitionSize(); idx++ { 142 if idx == wfr.PartitionSize() { 143 if idx != frameStartIdx { 144 t.Errorf("FrameStartIdx returned wrong result on Following: expected %+v, found %+v", idx, frameStartIdx) 145 t.Errorf("Search for %+v when wfr.RowIdx=%+v", value, wfr.RowIdx) 146 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 147 panic("") 148 } 149 break 150 } 151 valueAt, err := wfr.valueAt(evalCtx.Ctx(), idx) 152 if err != nil { 153 t.Errorf("unexpected error: %v", err) 154 } 155 if value.Compare(evalCtx, valueAt) <= 0 { 156 if idx != frameStartIdx { 157 t.Errorf("FrameStartIdx returned wrong result on Following: expected %+v, found %+v", idx, frameStartIdx) 158 t.Errorf("Search for %+v when wfr.RowIdx=%+v", value, wfr.RowIdx) 159 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 160 panic("") 161 } 162 break 163 } 164 } 165 } 166 } 167 } 168 169 func testEndPreceding( 170 t *testing.T, evalCtx *EvalContext, wfr *WindowFrameRun, offsetType *types.T, 171 ) { 172 wfr.Frame = &WindowFrame{ 173 Mode: RANGE, 174 Bounds: WindowFrameBounds{StartBound: &WindowFrameBound{BoundType: OffsetPreceding}, EndBound: &WindowFrameBound{BoundType: OffsetPreceding}}, 175 } 176 for offset := minOffset; offset < maxOffset; offset += rand.Intn(maxOffset / 10) { 177 var typedOffset Datum 178 switch offsetType.Family() { 179 case types.IntFamily: 180 typedOffset = NewDInt(DInt(offset)) 181 case types.FloatFamily: 182 typedOffset = NewDFloat(DFloat(offset)) 183 case types.DecimalFamily: 184 decimal := apd.Decimal{} 185 decimal.SetInt64(int64(offset)) 186 typedOffset = &DDecimal{Decimal: decimal} 187 default: 188 panic("unsupported offset type") 189 } 190 wfr.EndBoundOffset = typedOffset 191 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 192 frameEndIdx, err := wfr.FrameEndIdx(evalCtx.Ctx(), evalCtx) 193 if err != nil { 194 t.Errorf("unexpected error: %v", err) 195 } 196 value, err := wfr.getValueByOffset(evalCtx.Ctx(), evalCtx, typedOffset, true /* negative */) 197 if err != nil { 198 t.Errorf("unexpected error: %v", err) 199 } 200 for idx := wfr.PartitionSize() - 1; idx >= 0; idx-- { 201 valueAt, err := wfr.valueAt(evalCtx.Ctx(), idx) 202 if err != nil { 203 t.Errorf("unexpected error: %v", err) 204 } 205 if value.Compare(evalCtx, valueAt) >= 0 { 206 if idx+1 != frameEndIdx { 207 t.Errorf("FrameEndIdx returned wrong result on Preceding: expected %+v, found %+v", idx+1, frameEndIdx) 208 t.Errorf("Search for %+v when wfr.RowIdx=%+v", value, wfr.RowIdx) 209 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 210 panic("") 211 } 212 break 213 } 214 } 215 } 216 } 217 } 218 219 func testEndFollowing( 220 t *testing.T, evalCtx *EvalContext, wfr *WindowFrameRun, offsetType *types.T, 221 ) { 222 wfr.Frame = &WindowFrame{ 223 Mode: RANGE, 224 Bounds: WindowFrameBounds{StartBound: &WindowFrameBound{BoundType: OffsetPreceding}, EndBound: &WindowFrameBound{BoundType: OffsetFollowing}}, 225 } 226 for offset := minOffset; offset < maxOffset; offset += rand.Intn(maxOffset / 10) { 227 var typedOffset Datum 228 switch offsetType.Family() { 229 case types.IntFamily: 230 typedOffset = NewDInt(DInt(offset)) 231 case types.FloatFamily: 232 typedOffset = NewDFloat(DFloat(offset)) 233 case types.DecimalFamily: 234 decimal := apd.Decimal{} 235 decimal.SetInt64(int64(offset)) 236 typedOffset = &DDecimal{Decimal: decimal} 237 default: 238 panic("unsupported offset type") 239 } 240 wfr.EndBoundOffset = typedOffset 241 for wfr.RowIdx = 0; wfr.RowIdx < wfr.PartitionSize(); wfr.RowIdx++ { 242 frameEndIdx, err := wfr.FrameEndIdx(evalCtx.Ctx(), evalCtx) 243 if err != nil { 244 t.Errorf("unexpected error: %v", err) 245 } 246 value, err := wfr.getValueByOffset(evalCtx.Ctx(), evalCtx, typedOffset, false /* negative */) 247 if err != nil { 248 t.Errorf("unexpected error: %v", err) 249 } 250 for idx := wfr.PartitionSize() - 1; idx >= wfr.RowIdx; idx-- { 251 valueAt, err := wfr.valueAt(evalCtx.Ctx(), idx) 252 if err != nil { 253 t.Errorf("unexpected error: %v", err) 254 } 255 if value.Compare(evalCtx, valueAt) >= 0 { 256 if idx+1 != frameEndIdx { 257 t.Errorf("FrameEndIdx returned wrong result on Following: expected %+v, found %+v", idx+1, frameEndIdx) 258 t.Errorf("Search for %+v when wfr.RowIdx=%+v", value, wfr.RowIdx) 259 t.Errorf(partitionToString(evalCtx.Ctx(), wfr.Rows)) 260 panic("") 261 } 262 break 263 } 264 } 265 } 266 } 267 } 268 269 func makeIntSortedPartition(count int) indexedRows { 270 partition := indexedRows{rows: make([]indexedRow, count)} 271 r := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 272 number := 0 273 for idx := 0; idx < count; idx++ { 274 if r.Float64() < probabilityOfNewNumber { 275 number += r.Intn(10) 276 } 277 partition.rows[idx] = indexedRow{idx: idx, row: Datums{NewDInt(DInt(number))}} 278 } 279 return partition 280 } 281 282 func makeFloatSortedPartition(count int) indexedRows { 283 partition := indexedRows{rows: make([]indexedRow, count)} 284 r := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 285 number := 0.0 286 for idx := 0; idx < count; idx++ { 287 if r.Float64() < probabilityOfNewNumber { 288 number += r.Float64() * 10 289 } 290 partition.rows[idx] = indexedRow{idx: idx, row: Datums{NewDFloat(DFloat(number))}} 291 } 292 return partition 293 } 294 295 func makeDecimalSortedPartition(count int) indexedRows { 296 partition := indexedRows{rows: make([]indexedRow, count)} 297 r := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 298 number := &DDecimal{} 299 for idx := 0; idx < count; idx++ { 300 tmp := apd.Decimal{} 301 if r.Float64() < probabilityOfNewNumber { 302 _, err := tmp.SetFloat64(r.Float64() * 10) 303 if err != nil { 304 panic(fmt.Sprintf("unexpected error: %v", err)) 305 } 306 _, err = ExactCtx.Add(&number.Decimal, &number.Decimal, &tmp) 307 if err != nil { 308 panic(fmt.Sprintf("unexpected error: %v", err)) 309 } 310 } 311 value := &DDecimal{} 312 _, err := tmp.SetFloat64(0) 313 if err != nil { 314 panic(fmt.Sprintf("unexpected error: %v", err)) 315 } 316 _, err = ExactCtx.Add(&value.Decimal, &number.Decimal, &tmp) 317 if err != nil { 318 panic(fmt.Sprintf("unexpected error: %v", err)) 319 } 320 partition.rows[idx] = indexedRow{idx: idx, row: Datums{value}} 321 } 322 return partition 323 } 324 325 func partitionToString(ctx context.Context, partition IndexedRows) string { 326 var buffer bytes.Buffer 327 var err error 328 var row IndexedRow 329 buffer.WriteString("\n") 330 for idx := 0; idx < partition.Len(); idx++ { 331 if row, err = partition.GetRow(ctx, idx); err != nil { 332 return err.Error() 333 } 334 buffer.WriteString(fmt.Sprintf("%+v\n", row)) 335 } 336 return buffer.String() 337 } 338 339 func TestRangeMode(t *testing.T) { 340 defer leaktest.AfterTest(t)() 341 var counts = [...]int{1, 17, 42, 91} 342 for _, count := range counts { 343 testRangeMode(t, count) 344 } 345 } 346 347 // indexedRows are rows with the corresponding indices. 348 type indexedRows struct { 349 rows []indexedRow 350 } 351 352 // Len implements IndexedRows interface. 353 func (ir indexedRows) Len() int { 354 return len(ir.rows) 355 } 356 357 // GetRow implements IndexedRows interface. 358 func (ir indexedRows) GetRow(_ context.Context, idx int) (IndexedRow, error) { 359 return ir.rows[idx], nil 360 } 361 362 // indexedRow is a row with a corresponding index. 363 type indexedRow struct { 364 idx int 365 row Datums 366 } 367 368 // GetIdx implements IndexedRow interface. 369 func (ir indexedRow) GetIdx() int { 370 return ir.idx 371 } 372 373 // GetDatum implements IndexedRow interface. 374 func (ir indexedRow) GetDatum(colIdx int) (Datum, error) { 375 return ir.row[colIdx], nil 376 } 377 378 // GetDatums implements IndexedRow interface. 379 func (ir indexedRow) GetDatums(firstColIdx, lastColIdx int) (Datums, error) { 380 return ir.row[firstColIdx:lastColIdx], nil 381 } 382 383 func (ir indexedRow) String() string { 384 return fmt.Sprintf("%d: %s", ir.idx, ir.row.String()) 385 }