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  }