github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/window_functions_test.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  package colexec
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/colexecbase"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    25  	"github.com/cockroachdb/cockroach/pkg/testutils/colcontainerutils"
    26  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    27  	"github.com/cockroachdb/cockroach/pkg/util/mon"
    28  	"github.com/marusama/semaphore"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  type windowFnTestCase struct {
    33  	tuples       []tuple
    34  	expected     []tuple
    35  	windowerSpec execinfrapb.WindowerSpec
    36  }
    37  
    38  func (tc *windowFnTestCase) init() {
    39  	for i := range tc.windowerSpec.WindowFns {
    40  		tc.windowerSpec.WindowFns[i].FilterColIdx = noFilterIdx
    41  	}
    42  }
    43  
    44  func TestWindowFunctions(t *testing.T) {
    45  	defer leaktest.AfterTest(t)()
    46  	ctx := context.Background()
    47  	st := cluster.MakeTestingClusterSettings()
    48  	evalCtx := tree.MakeTestingEvalContext(st)
    49  	defer evalCtx.Stop(ctx)
    50  	evalCtx.SessionData.VectorizeMode = sessiondata.VectorizeOn
    51  	flowCtx := &execinfra.FlowCtx{
    52  		EvalCtx: &evalCtx,
    53  		Cfg: &execinfra.ServerConfig{
    54  			Settings:    st,
    55  			DiskMonitor: testDiskMonitor,
    56  		},
    57  	}
    58  	// All supported window function operators will use from 0 to 3 disk queues
    59  	// with each using a single FD at any point in time. Additionally, the
    60  	// disk-backed sorter (that will be planned depending on PARTITION BY and
    61  	// ORDER BY combinations) will be limited to this number using a testing
    62  	// knob, so 3 is necessary and sufficient.
    63  	const maxNumberFDs = 3
    64  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
    65  	defer cleanup()
    66  
    67  	rowNumberFn := execinfrapb.WindowerSpec_ROW_NUMBER
    68  	rankFn := execinfrapb.WindowerSpec_RANK
    69  	denseRankFn := execinfrapb.WindowerSpec_DENSE_RANK
    70  	percentRankFn := execinfrapb.WindowerSpec_PERCENT_RANK
    71  	cumeDistFn := execinfrapb.WindowerSpec_CUME_DIST
    72  	accounts := make([]*mon.BoundAccount, 0)
    73  	monitors := make([]*mon.BytesMonitor, 0)
    74  	for _, spillForced := range []bool{false, true} {
    75  		flowCtx.Cfg.TestingKnobs.ForceDiskSpill = spillForced
    76  		for _, tc := range []windowFnTestCase{
    77  			// With PARTITION BY, no ORDER BY.
    78  			{
    79  				tuples:   tuples{{1}, {1}, {1}, {2}, {2}, {3}},
    80  				expected: tuples{{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {3, 1}},
    81  				windowerSpec: execinfrapb.WindowerSpec{
    82  					PartitionBy: []uint32{0},
    83  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
    84  						{
    85  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rowNumberFn},
    86  							OutputColIdx: 1,
    87  						},
    88  					},
    89  				},
    90  			},
    91  			{
    92  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
    93  				expected: tuples{{nil, 1}, {nil, 1}, {1, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 1}},
    94  				windowerSpec: execinfrapb.WindowerSpec{
    95  					PartitionBy: []uint32{0},
    96  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
    97  						{
    98  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rankFn},
    99  							OutputColIdx: 1,
   100  						},
   101  					},
   102  				},
   103  			},
   104  			{
   105  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   106  				expected: tuples{{nil, 1}, {nil, 1}, {1, 1}, {1, 1}, {2, 1}, {3, 1}, {3, 1}},
   107  				windowerSpec: execinfrapb.WindowerSpec{
   108  					PartitionBy: []uint32{0},
   109  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   110  						{
   111  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &denseRankFn},
   112  							OutputColIdx: 1,
   113  						},
   114  					},
   115  				},
   116  			},
   117  			{
   118  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   119  				expected: tuples{{nil, 0}, {nil, 0}, {1, 0}, {1, 0}, {2, 0}, {3, 0}, {3, 0}},
   120  				windowerSpec: execinfrapb.WindowerSpec{
   121  					PartitionBy: []uint32{0},
   122  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   123  						{
   124  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &percentRankFn},
   125  							OutputColIdx: 1,
   126  						},
   127  					},
   128  				},
   129  			},
   130  			{
   131  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   132  				expected: tuples{{nil, 1.0}, {nil, 1.0}, {1, 1.0}, {1, 1.0}, {2, 1.0}, {3, 1.0}, {3, 1.0}},
   133  				windowerSpec: execinfrapb.WindowerSpec{
   134  					PartitionBy: []uint32{0},
   135  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   136  						{
   137  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &cumeDistFn},
   138  							OutputColIdx: 1,
   139  						},
   140  					},
   141  				},
   142  			},
   143  
   144  			// No PARTITION BY, with ORDER BY.
   145  			{
   146  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   147  				expected: tuples{{nil, 1}, {nil, 2}, {1, 3}, {1, 4}, {2, 5}, {3, 6}, {3, 7}},
   148  				windowerSpec: execinfrapb.WindowerSpec{
   149  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   150  						{
   151  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rowNumberFn},
   152  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
   153  							OutputColIdx: 1,
   154  						},
   155  					},
   156  				},
   157  			},
   158  			{
   159  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   160  				expected: tuples{{nil, 1}, {nil, 1}, {1, 3}, {1, 3}, {2, 5}, {3, 6}, {3, 6}},
   161  				windowerSpec: execinfrapb.WindowerSpec{
   162  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   163  						{
   164  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rankFn},
   165  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
   166  							OutputColIdx: 1,
   167  						},
   168  					},
   169  				},
   170  			},
   171  			{
   172  				tuples:   tuples{{3}, {1}, {2}, {nil}, {1}, {nil}, {3}},
   173  				expected: tuples{{nil, 1}, {nil, 1}, {1, 2}, {1, 2}, {2, 3}, {3, 4}, {3, 4}},
   174  				windowerSpec: execinfrapb.WindowerSpec{
   175  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   176  						{
   177  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &denseRankFn},
   178  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
   179  							OutputColIdx: 1,
   180  						},
   181  					},
   182  				},
   183  			},
   184  			{
   185  				tuples:   tuples{{3}, {1}, {2}, {1}, {nil}, {1}, {nil}, {3}},
   186  				expected: tuples{{nil, 0}, {nil, 0}, {1, 2.0 / 7}, {1, 2.0 / 7}, {1, 2.0 / 7}, {2, 5.0 / 7}, {3, 6.0 / 7}, {3, 6.0 / 7}},
   187  				windowerSpec: execinfrapb.WindowerSpec{
   188  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   189  						{
   190  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &percentRankFn},
   191  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
   192  							OutputColIdx: 1,
   193  						},
   194  					},
   195  				},
   196  			},
   197  			{
   198  				tuples:   tuples{{3}, {1}, {2}, {1}, {nil}, {1}, {nil}, {3}},
   199  				expected: tuples{{nil, 2.0 / 8}, {nil, 2.0 / 8}, {1, 5.0 / 8}, {1, 5.0 / 8}, {1, 5.0 / 8}, {2, 6.0 / 8}, {3, 1.0}, {3, 1.0}},
   200  				windowerSpec: execinfrapb.WindowerSpec{
   201  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   202  						{
   203  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &cumeDistFn},
   204  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0}}},
   205  							OutputColIdx: 1,
   206  						},
   207  					},
   208  				},
   209  			},
   210  
   211  			// With both PARTITION BY and ORDER BY.
   212  			{
   213  				tuples:   tuples{{3, 2}, {1, nil}, {2, 1}, {nil, nil}, {1, 2}, {nil, 1}, {nil, nil}, {3, 1}},
   214  				expected: tuples{{nil, nil, 1}, {nil, nil, 2}, {nil, 1, 3}, {1, nil, 1}, {1, 2, 2}, {2, 1, 1}, {3, 1, 1}, {3, 2, 2}},
   215  				windowerSpec: execinfrapb.WindowerSpec{
   216  					PartitionBy: []uint32{0},
   217  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   218  						{
   219  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rowNumberFn},
   220  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 1}}},
   221  							OutputColIdx: 2,
   222  						},
   223  					},
   224  				},
   225  			},
   226  			{
   227  				tuples:   tuples{{3, 2}, {1, nil}, {2, 1}, {nil, nil}, {1, 2}, {nil, 1}, {nil, nil}, {3, 1}},
   228  				expected: tuples{{nil, nil, 1}, {nil, nil, 1}, {nil, 1, 3}, {1, nil, 1}, {1, 2, 2}, {2, 1, 1}, {3, 1, 1}, {3, 2, 2}},
   229  				windowerSpec: execinfrapb.WindowerSpec{
   230  					PartitionBy: []uint32{0},
   231  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   232  						{
   233  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &rankFn},
   234  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 1}}},
   235  							OutputColIdx: 2,
   236  						},
   237  					},
   238  				},
   239  			},
   240  			{
   241  				tuples:   tuples{{3, 2}, {1, nil}, {2, 1}, {nil, nil}, {1, 2}, {nil, 1}, {nil, nil}, {3, 1}},
   242  				expected: tuples{{nil, nil, 1}, {nil, nil, 1}, {nil, 1, 2}, {1, nil, 1}, {1, 2, 2}, {2, 1, 1}, {3, 1, 1}, {3, 2, 2}},
   243  				windowerSpec: execinfrapb.WindowerSpec{
   244  					PartitionBy: []uint32{0},
   245  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   246  						{
   247  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &denseRankFn},
   248  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 1}}},
   249  							OutputColIdx: 2,
   250  						},
   251  					},
   252  				},
   253  			},
   254  			{
   255  				tuples:   tuples{{nil, 2}, {3, 2}, {1, nil}, {2, 1}, {nil, nil}, {1, 2}, {nil, 1}, {1, 3}, {nil, nil}, {3, 1}},
   256  				expected: tuples{{nil, nil, 0}, {nil, nil, 0}, {nil, 1, 2.0 / 3}, {nil, 2, 1}, {1, nil, 0}, {1, 2, 1.0 / 2}, {1, 3, 1}, {2, 1, 0}, {3, 1, 0}, {3, 2, 1}},
   257  				windowerSpec: execinfrapb.WindowerSpec{
   258  					PartitionBy: []uint32{0},
   259  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   260  						{
   261  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &percentRankFn},
   262  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 1}}},
   263  							OutputColIdx: 2,
   264  						},
   265  					},
   266  				},
   267  			},
   268  			{
   269  				tuples:   tuples{{nil, 2}, {3, 2}, {1, nil}, {2, 1}, {nil, nil}, {1, 2}, {nil, 1}, {1, 3}, {nil, nil}, {3, 1}},
   270  				expected: tuples{{nil, nil, 2.0 / 4}, {nil, nil, 2.0 / 4}, {nil, 1, 3.0 / 4}, {nil, 2, 1}, {1, nil, 1.0 / 3}, {1, 2, 2.0 / 3}, {1, 3, 1}, {2, 1, 1}, {3, 1, 1.0 / 2}, {3, 2, 1}},
   271  				windowerSpec: execinfrapb.WindowerSpec{
   272  					PartitionBy: []uint32{0},
   273  					WindowFns: []execinfrapb.WindowerSpec_WindowFn{
   274  						{
   275  							Func:         execinfrapb.WindowerSpec_Func{WindowFunc: &cumeDistFn},
   276  							Ordering:     execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 1}}},
   277  							OutputColIdx: 2,
   278  						},
   279  					},
   280  				},
   281  			},
   282  		} {
   283  			t.Run(fmt.Sprintf("spillForced=%t/%s", spillForced, tc.windowerSpec.WindowFns[0].Func.String()), func(t *testing.T) {
   284  				var semsToCheck []semaphore.Semaphore
   285  				runTests(t, []tuples{tc.tuples}, tc.expected, unorderedVerifier, func(inputs []colexecbase.Operator) (colexecbase.Operator, error) {
   286  					tc.init()
   287  					ct := make([]*types.T, len(tc.tuples[0]))
   288  					for i := range ct {
   289  						ct[i] = types.Int
   290  					}
   291  					spec := &execinfrapb.ProcessorSpec{
   292  						Input: []execinfrapb.InputSyncSpec{{ColumnTypes: ct}},
   293  						Core: execinfrapb.ProcessorCoreUnion{
   294  							Windower: &tc.windowerSpec,
   295  						},
   296  					}
   297  					sem := colexecbase.NewTestingSemaphore(maxNumberFDs)
   298  					args := NewColOperatorArgs{
   299  						Spec:                spec,
   300  						Inputs:              inputs,
   301  						StreamingMemAccount: testMemAcc,
   302  						DiskQueueCfg:        queueCfg,
   303  						FDSemaphore:         sem,
   304  					}
   305  					semsToCheck = append(semsToCheck, sem)
   306  					args.TestingKnobs.UseStreamingMemAccountForBuffering = true
   307  					args.TestingKnobs.NumForcedRepartitions = maxNumberFDs
   308  					result, err := NewColOperator(ctx, flowCtx, args)
   309  					accounts = append(accounts, result.OpAccounts...)
   310  					monitors = append(monitors, result.OpMonitors...)
   311  					return result.Op, err
   312  				})
   313  				for i, sem := range semsToCheck {
   314  					require.Equal(t, 0, sem.GetCount(), "sem still reports open FDs at index %d", i)
   315  				}
   316  			})
   317  		}
   318  	}
   319  
   320  	for _, acc := range accounts {
   321  		acc.Close(ctx)
   322  	}
   323  
   324  	for _, m := range monitors {
   325  		m.Stop(ctx)
   326  	}
   327  }