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 }