github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/relative_rank_tmpl.go (about) 1 // Copyright 2020 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 // {{/* 12 // +build execgen_template 13 // 14 // This file is the execgen template for relative_rank.eg.go. It's formatted in 15 // a special way, so it's both valid Go and a valid text/template input. This 16 // permits editing this file with editor support. 17 // 18 // */}} 19 20 package colexec 21 22 import ( 23 "context" 24 25 "github.com/cockroachdb/cockroach/pkg/col/coldata" 26 "github.com/cockroachdb/cockroach/pkg/sql/colcontainer" 27 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 28 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror" 29 "github.com/cockroachdb/cockroach/pkg/sql/colmem" 30 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 31 "github.com/cockroachdb/cockroach/pkg/sql/types" 32 "github.com/cockroachdb/cockroach/pkg/util/mon" 33 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 34 "github.com/cockroachdb/errors" 35 "github.com/marusama/semaphore" 36 ) 37 38 // TODO(yuzefovich): add benchmarks. 39 40 // NewRelativeRankOperator creates a new Operator that computes window 41 // functions PERCENT_RANK or CUME_DIST (depending on the passed in windowFn). 42 // outputColIdx specifies in which coldata.Vec the operator should put its 43 // output (if there is no such column, a new column is appended). 44 func NewRelativeRankOperator( 45 unlimitedAllocator *colmem.Allocator, 46 memoryLimit int64, 47 diskQueueCfg colcontainer.DiskQueueCfg, 48 fdSemaphore semaphore.Semaphore, 49 input colexecbase.Operator, 50 inputTypes []*types.T, 51 windowFn execinfrapb.WindowerSpec_WindowFunc, 52 orderingCols []execinfrapb.Ordering_Column, 53 outputColIdx int, 54 partitionColIdx int, 55 peersColIdx int, 56 diskAcc *mon.BoundAccount, 57 ) (colexecbase.Operator, error) { 58 if len(orderingCols) == 0 { 59 constValue := float64(0) 60 if windowFn == execinfrapb.WindowerSpec_CUME_DIST { 61 constValue = 1 62 } 63 return NewConstOp(unlimitedAllocator, input, types.Float, constValue, outputColIdx) 64 } 65 rrInitFields := relativeRankInitFields{ 66 rankInitFields: rankInitFields{ 67 OneInputNode: NewOneInputNode(input), 68 allocator: unlimitedAllocator, 69 outputColIdx: outputColIdx, 70 partitionColIdx: partitionColIdx, 71 peersColIdx: peersColIdx, 72 }, 73 memoryLimit: memoryLimit, 74 diskQueueCfg: diskQueueCfg, 75 fdSemaphore: fdSemaphore, 76 inputTypes: inputTypes, 77 diskAcc: diskAcc, 78 } 79 switch windowFn { 80 case execinfrapb.WindowerSpec_PERCENT_RANK: 81 if partitionColIdx != columnOmitted { 82 return &percentRankWithPartitionOp{ 83 relativeRankInitFields: rrInitFields, 84 }, nil 85 } 86 return &percentRankNoPartitionOp{ 87 relativeRankInitFields: rrInitFields, 88 }, nil 89 case execinfrapb.WindowerSpec_CUME_DIST: 90 if partitionColIdx != columnOmitted { 91 return &cumeDistWithPartitionOp{ 92 relativeRankInitFields: rrInitFields, 93 }, nil 94 } 95 return &cumeDistNoPartitionOp{ 96 relativeRankInitFields: rrInitFields, 97 }, nil 98 default: 99 return nil, errors.Errorf("unsupported relative rank type %s", windowFn) 100 } 101 } 102 103 // NOTE: in the context of window functions "partitions" mean a different thing 104 // from "partition" in the context of external algorithms and some disk 105 // infrastructure: here, "partitions" are sets of tuples that are not distinct 106 // on the columns specified in PARTITION BY clause of the window function. If 107 // such clause is omitted, then all tuples from the input belong to the same 108 // partition. 109 110 type relativeRankState int 111 112 const ( 113 // relativeRankBuffering is the state in which relativeRank operators fully 114 // buffer their input using spillingQueue. Additionally, the operators will 115 // be computing the sizes of the partitions and peer groups (if needed) 116 // using separate spillingQueues for each. Once a zero-length batch is 117 // received, the operator transitions to relativeRankEmitting state. 118 relativeRankBuffering relativeRankState = iota 119 // relativeRankEmitting is the state in which relativeRank operators emit 120 // the output. The output batch is populated by copying the next batch from 121 // the "buffered tuples" spilling queue and manually computing the output 122 // column for the window function using the already computed sizes of 123 // partitions and peer groups. Once a zero-length batch is dequeued from 124 // the "buffered tuples" queue, the operator transitions to 125 // relativeRankFinished state. 126 relativeRankEmitting 127 // relativeRankFinished is the state in which relativeRank operators close 128 // any non-closed disk resources and emit the zero-length batch. 129 relativeRankFinished 130 ) 131 132 // {{/* 133 // _COMPUTE_PARTITIONS_SIZES is a code snippet that computes the sizes of 134 // partitions. It looks at i'th partitionCol value to check whether a new 135 // partition begins at index i, and if so, it records the already computed 136 // size of the previous partition into partitionsState.runningSizes vector. 137 func _COMPUTE_PARTITIONS_SIZES() { // */}} 138 // {{define "computePartitionsSizes" -}} 139 if partitionCol[i] { 140 // We have encountered a start of a new partition, so we 141 // need to save the computed size of the previous one 142 // (if there was one). 143 if r.partitionsState.runningSizes == nil { 144 // TODO(yuzefovich): do not instantiate a new batch here once 145 // spillingQueues actually copy the batches when those are kept 146 // in-memory. 147 r.partitionsState.runningSizes = r.allocator.NewMemBatch([]*types.T{types.Int}) 148 runningPartitionsSizesCol = r.partitionsState.runningSizes.ColVec(0).Int64() 149 } 150 if r.numTuplesInPartition > 0 { 151 runningPartitionsSizesCol[r.partitionsState.idx] = r.numTuplesInPartition 152 r.numTuplesInPartition = 0 153 r.partitionsState.idx++ 154 if r.partitionsState.idx == coldata.BatchSize() { 155 // We need to flush the vector of partitions sizes. 156 r.partitionsState.runningSizes.SetLength(coldata.BatchSize()) 157 if err := r.partitionsState.enqueue(ctx, r.partitionsState.runningSizes); err != nil { 158 colexecerror.InternalError(err) 159 } 160 r.partitionsState.runningSizes = nil 161 r.partitionsState.idx = 0 162 } 163 } 164 } 165 r.numTuplesInPartition++ 166 // {{end}} 167 // {{/* 168 } // */}} 169 170 // {{/* 171 // _COMPUTE_PEER_GROUPS_SIZES is a code snippet that computes the sizes of 172 // peer groups. It looks at i'th peersCol value to check whether a new 173 // peer group begins at index i, and if so, it records the already computed 174 // size of the previous peer group into peerGroupsState.runningSizes vector. 175 func _COMPUTE_PEER_GROUPS_SIZES() { // */}} 176 // {{define "computePeerGroupsSizes" -}} 177 if peersCol[i] { 178 // We have encountered a start of a new peer group, so we 179 // need to save the computed size of the previous one 180 // (if there was one). 181 if r.peerGroupsState.runningSizes == nil { 182 // TODO(yuzefovich): do not instantiate a new batch here once 183 // spillingQueues actually copy the batches when those are kept 184 // in-memory. 185 r.peerGroupsState.runningSizes = r.allocator.NewMemBatch([]*types.T{types.Int}) 186 runningPeerGroupsSizesCol = r.peerGroupsState.runningSizes.ColVec(0).Int64() 187 } 188 if r.numPeers > 0 { 189 runningPeerGroupsSizesCol[r.peerGroupsState.idx] = r.numPeers 190 r.numPeers = 0 191 r.peerGroupsState.idx++ 192 if r.peerGroupsState.idx == coldata.BatchSize() { 193 // We need to flush the vector of peer group sizes. 194 r.peerGroupsState.runningSizes.SetLength(coldata.BatchSize()) 195 if err := r.peerGroupsState.enqueue(ctx, r.peerGroupsState.runningSizes); err != nil { 196 colexecerror.InternalError(err) 197 } 198 r.peerGroupsState.runningSizes = nil 199 r.peerGroupsState.idx = 0 200 } 201 } 202 } 203 r.numPeers++ 204 // {{end}} 205 // {{/* 206 } // */}} 207 208 type relativeRankInitFields struct { 209 rankInitFields 210 closerHelper 211 212 state relativeRankState 213 memoryLimit int64 214 diskQueueCfg colcontainer.DiskQueueCfg 215 fdSemaphore semaphore.Semaphore 216 inputTypes []*types.T 217 218 diskAcc *mon.BoundAccount 219 } 220 221 type relativeRankSizesState struct { 222 *spillingQueue 223 224 // runningSizes is a batch consisting of a single int64 vector that stores 225 // sizes while we're computing them. Once all coldata.BatchSize() slots are 226 // filled, it will be flushed to the spillingQueue. 227 runningSizes coldata.Batch 228 // dequeuedSizes is a batch of already computed sizes that is dequeued 229 // from the spillingQueue. 230 dequeuedSizes coldata.Batch 231 // idx stores the index of the current slot in one of the batches above 232 // that we're currently working with. 233 idx int 234 } 235 236 // relativeRankUtilityQueueMemLimitFraction defines the fraction of the memory 237 // limit that will be given to the "utility" spillingQueues of relativeRank 238 // operators (i.e. non "buffered tuples" queues). 239 const relativeRankUtilityQueueMemLimitFraction = 0.1 240 241 // {{range .}} 242 243 type _RELATIVE_RANK_STRINGOp struct { 244 relativeRankInitFields 245 246 // mu is used to protect against concurrent IdempotentClose and Next calls, 247 // which are currently allowed. 248 // TODO(asubiotto): Explore calling IdempotentClose from the same goroutine as 249 // Next, which will simplify this model. 250 mu syncutil.Mutex 251 252 // {{if .IsPercentRank}} 253 // rank indicates which rank should be assigned to the next tuple. 254 rank int64 255 // rankIncrement indicates by how much rank should be incremented when a 256 // tuple distinct from the previous one on the ordering columns is seen. 257 rankIncrement int64 258 // {{end}} 259 260 // {{if .IsCumeDist}} 261 peerGroupsState relativeRankSizesState 262 // numPrecedingTuples stores the number of tuples preceding to the first 263 // peer of the current tuple in the current partition. 264 numPrecedingTuples int64 265 // numPeers stores the number of tuples that are peers with the current 266 // tuple. 267 numPeers int64 268 // {{end}} 269 270 // {{if .HasPartition}} 271 partitionsState relativeRankSizesState 272 // {{end}} 273 // numTuplesInPartition contains the number of tuples in the current 274 // partition. 275 numTuplesInPartition int64 276 277 bufferedTuples *spillingQueue 278 scratch coldata.Batch 279 output coldata.Batch 280 } 281 282 var _ closableOperator = &_RELATIVE_RANK_STRINGOp{} 283 284 func (r *_RELATIVE_RANK_STRINGOp) Init() { 285 r.Input().Init() 286 r.state = relativeRankBuffering 287 usedMemoryLimitFraction := 0.0 288 // {{if .HasPartition}} 289 r.partitionsState.spillingQueue = newSpillingQueue( 290 r.allocator, []*types.T{types.Int}, 291 int64(float64(r.memoryLimit)*relativeRankUtilityQueueMemLimitFraction), 292 r.diskQueueCfg, r.fdSemaphore, coldata.BatchSize(), r.diskAcc, 293 ) 294 usedMemoryLimitFraction += relativeRankUtilityQueueMemLimitFraction 295 // {{end}} 296 // {{if .IsCumeDist}} 297 r.peerGroupsState.spillingQueue = newSpillingQueue( 298 r.allocator, []*types.T{types.Int}, 299 int64(float64(r.memoryLimit)*relativeRankUtilityQueueMemLimitFraction), 300 r.diskQueueCfg, r.fdSemaphore, coldata.BatchSize(), r.diskAcc, 301 ) 302 usedMemoryLimitFraction += relativeRankUtilityQueueMemLimitFraction 303 // {{end}} 304 r.bufferedTuples = newSpillingQueue( 305 r.allocator, r.inputTypes, 306 int64(float64(r.memoryLimit)*(1.0-usedMemoryLimitFraction)), 307 r.diskQueueCfg, r.fdSemaphore, coldata.BatchSize(), r.diskAcc, 308 ) 309 r.output = r.allocator.NewMemBatch(append(r.inputTypes, types.Float)) 310 // {{if .IsPercentRank}} 311 // All rank functions start counting from 1. Before we assign the rank to a 312 // tuple in the batch, we first increment r.rank, so setting this 313 // rankIncrement to 1 will update r.rank to 1 on the very first tuple (as 314 // desired). 315 r.rankIncrement = 1 316 // {{end}} 317 } 318 319 func (r *_RELATIVE_RANK_STRINGOp) Next(ctx context.Context) coldata.Batch { 320 r.mu.Lock() 321 defer r.mu.Unlock() 322 var err error 323 for { 324 switch r.state { 325 case relativeRankBuffering: 326 // The outline of what we need to do in "buffering" state: 327 // 328 // 1. we need to buffer the tuples that we read from the input. 329 // These are simply copied into r.bufferedTuples spillingQueue. 330 // 331 // 2. (if we have PARTITION BY clause) we need to compute the sizes of 332 // partitions. These sizes are stored in r.partitionsState.runningSizes 333 // batch (that consists of a single vector) and r.partitionsState.idx 334 // points at the next slot in that vector to write to. Once it 335 // reaches coldata.BatchSize(), the batch is "flushed" to the 336 // corresponding spillingQueue. The "running" value of the current 337 // partition size is stored in r.numTuplesInPartition. 338 // 339 // 3. (if we have CUME_DIST function) we need to compute the sizes 340 // of peer groups. These sizes are stored in r.peerGroupsState.runningSizes 341 // batch (that consists of a single vector) and r.peerGroupsState.idx 342 // points at the next slot in that vector to write to. Once it 343 // reaches coldata.BatchSize(), the batch is "flushed" to the 344 // corresponding spillingQueue. The "running" value of the current 345 // peer group size is stored in r.numPeers. 346 // 347 // For example, if we have the following setup: 348 // partitionCol = {true, false, false, true, false, false, false, false} 349 // peersCol = {true, false, true, true, false, false, true, false} 350 // we want this as the result: 351 // partitionsSizes = {3, 5} 352 // peerGroupsSizes = {2, 1, 3, 2}. 353 // This example also shows why we need to use two different queues 354 // (since every partition can have multiple peer groups, the 355 // schedule of "flushing" is different). 356 batch := r.Input().Next(ctx) 357 n := batch.Length() 358 if n == 0 { 359 if err := r.bufferedTuples.enqueue(ctx, coldata.ZeroBatch); err != nil { 360 colexecerror.InternalError(err) 361 } 362 // {{if .HasPartition}} 363 // We need to flush the last vector of the running partitions 364 // sizes, including the very last partition. 365 if r.partitionsState.runningSizes == nil { 366 // TODO(yuzefovich): do not instantiate a new batch here once 367 // spillingQueues actually copy the batches when those are kept 368 // in-memory. 369 r.partitionsState.runningSizes = r.allocator.NewMemBatch([]*types.T{types.Int}) 370 } 371 runningPartitionsSizesCol := r.partitionsState.runningSizes.ColVec(0).Int64() 372 runningPartitionsSizesCol[r.partitionsState.idx] = r.numTuplesInPartition 373 r.partitionsState.idx++ 374 r.partitionsState.runningSizes.SetLength(r.partitionsState.idx) 375 if err := r.partitionsState.enqueue(ctx, r.partitionsState.runningSizes); err != nil { 376 colexecerror.InternalError(err) 377 } 378 if err := r.partitionsState.enqueue(ctx, coldata.ZeroBatch); err != nil { 379 colexecerror.InternalError(err) 380 } 381 // {{end}} 382 // {{if .IsCumeDist}} 383 // We need to flush the last vector of the running peer groups 384 // sizes, including the very last peer group. 385 if r.peerGroupsState.runningSizes == nil { 386 // TODO(yuzefovich): do not instantiate a new batch here once 387 // spillingQueues actually copy the batches when those are kept 388 // in-memory. 389 r.peerGroupsState.runningSizes = r.allocator.NewMemBatch([]*types.T{types.Int}) 390 } 391 runningPeerGroupsSizesCol := r.peerGroupsState.runningSizes.ColVec(0).Int64() 392 runningPeerGroupsSizesCol[r.peerGroupsState.idx] = r.numPeers 393 r.peerGroupsState.idx++ 394 r.peerGroupsState.runningSizes.SetLength(r.peerGroupsState.idx) 395 if err := r.peerGroupsState.enqueue(ctx, r.peerGroupsState.runningSizes); err != nil { 396 colexecerror.InternalError(err) 397 } 398 if err := r.peerGroupsState.enqueue(ctx, coldata.ZeroBatch); err != nil { 399 colexecerror.InternalError(err) 400 } 401 // {{end}} 402 // We have fully consumed the input, so now we can populate the output. 403 r.state = relativeRankEmitting 404 continue 405 } 406 407 // {{if .HasPartition}} 408 // For simplicity, we will fully consume the input before we start 409 // producing the output. 410 // TODO(yuzefovich): we could be emitting output once we see that a new 411 // partition has begun. 412 // {{else}} 413 // All tuples belong to the same partition, so we need to fully consume 414 // the input before we can proceed. 415 // {{end}} 416 417 sel := batch.Selection() 418 // First, we buffer up all of the tuples. 419 // TODO(yuzefovich): do not instantiate a new batch here once 420 // spillingQueues actually copy the batches when those are kept 421 // in-memory. 422 r.scratch = r.allocator.NewMemBatchWithSize(r.inputTypes, n) 423 r.allocator.PerformOperation(r.scratch.ColVecs(), func() { 424 for colIdx, vec := range r.scratch.ColVecs() { 425 vec.Copy( 426 coldata.CopySliceArgs{ 427 SliceArgs: coldata.SliceArgs{ 428 Src: batch.ColVec(colIdx), 429 Sel: sel, 430 SrcEndIdx: n, 431 }, 432 }, 433 ) 434 } 435 r.scratch.SetLength(n) 436 }) 437 if err := r.bufferedTuples.enqueue(ctx, r.scratch); err != nil { 438 colexecerror.InternalError(err) 439 } 440 441 // Then, we need to update the sizes of the partitions. 442 // {{if .HasPartition}} 443 partitionCol := batch.ColVec(r.partitionColIdx).Bool() 444 var runningPartitionsSizesCol []int64 445 if r.partitionsState.runningSizes != nil { 446 runningPartitionsSizesCol = r.partitionsState.runningSizes.ColVec(0).Int64() 447 } 448 if sel != nil { 449 for _, i := range sel[:n] { 450 _COMPUTE_PARTITIONS_SIZES() 451 } 452 } else { 453 for i := 0; i < n; i++ { 454 _COMPUTE_PARTITIONS_SIZES() 455 } 456 } 457 // {{else}} 458 // There is a single partition in the whole input. 459 r.numTuplesInPartition += int64(n) 460 // {{end}} 461 462 // {{if .IsCumeDist}} 463 // Next, we need to update the sizes of the peer groups. 464 peersCol := batch.ColVec(r.peersColIdx).Bool() 465 var runningPeerGroupsSizesCol []int64 466 if r.peerGroupsState.runningSizes != nil { 467 runningPeerGroupsSizesCol = r.peerGroupsState.runningSizes.ColVec(0).Int64() 468 } 469 if sel != nil { 470 for _, i := range sel[:n] { 471 _COMPUTE_PEER_GROUPS_SIZES() 472 } 473 } else { 474 for i := 0; i < n; i++ { 475 _COMPUTE_PEER_GROUPS_SIZES() 476 } 477 } 478 // {{end}} 479 continue 480 481 case relativeRankEmitting: 482 if r.scratch, err = r.bufferedTuples.dequeue(ctx); err != nil { 483 colexecerror.InternalError(err) 484 } 485 n := r.scratch.Length() 486 if n == 0 { 487 r.state = relativeRankFinished 488 continue 489 } 490 // {{if .HasPartition}} 491 // Get the next batch of partition sizes if we haven't already. 492 if r.partitionsState.dequeuedSizes == nil { 493 if r.partitionsState.dequeuedSizes, err = r.partitionsState.dequeue(ctx); err != nil { 494 colexecerror.InternalError(err) 495 } 496 r.partitionsState.idx = 0 497 r.numTuplesInPartition = 0 498 } 499 // {{end}} 500 // {{if .IsCumeDist}} 501 // Get the next batch of peer group sizes if we haven't already. 502 if r.peerGroupsState.dequeuedSizes == nil { 503 if r.peerGroupsState.dequeuedSizes, err = r.peerGroupsState.dequeue(ctx); err != nil { 504 colexecerror.InternalError(err) 505 } 506 r.peerGroupsState.idx = 0 507 r.numPeers = 0 508 } 509 // {{end}} 510 511 r.output.ResetInternalBatch() 512 // First, we copy over the buffered up columns. 513 r.allocator.PerformOperation(r.output.ColVecs()[:len(r.inputTypes)], func() { 514 for colIdx, vec := range r.output.ColVecs()[:len(r.inputTypes)] { 515 vec.Copy( 516 coldata.CopySliceArgs{ 517 SliceArgs: coldata.SliceArgs{ 518 Src: r.scratch.ColVec(colIdx), 519 SrcEndIdx: n, 520 }, 521 }, 522 ) 523 } 524 }) 525 526 // Now we will populate the output column. 527 relativeRankOutputCol := r.output.ColVec(r.outputColIdx).Float64() 528 // {{if .HasPartition}} 529 partitionCol := r.scratch.ColVec(r.partitionColIdx).Bool() 530 // {{end}} 531 peersCol := r.scratch.ColVec(r.peersColIdx).Bool() 532 // We don't need to think about the selection vector since all the 533 // buffered up tuples have been "deselected" during the buffering 534 // stage. 535 for i := range relativeRankOutputCol[:n] { 536 // We need to set r.numTuplesInPartition to the size of the 537 // partition that i'th tuple belongs to (which we have already 538 // computed). 539 // {{if .HasPartition}} 540 if partitionCol[i] { 541 if r.partitionsState.idx == r.partitionsState.dequeuedSizes.Length() { 542 if r.partitionsState.dequeuedSizes, err = r.partitionsState.dequeue(ctx); err != nil { 543 colexecerror.InternalError(err) 544 } 545 r.partitionsState.idx = 0 546 } 547 r.numTuplesInPartition = r.partitionsState.dequeuedSizes.ColVec(0).Int64()[r.partitionsState.idx] 548 r.partitionsState.idx++ 549 // {{if .IsPercentRank}} 550 // We need to reset the internal state because of the new 551 // partition. 552 r.rank = 0 553 r.rankIncrement = 1 554 // {{end}} 555 // {{if .IsCumeDist}} 556 // We need to reset the number of preceding tuples because of the 557 // new partition. 558 r.numPrecedingTuples = 0 559 r.numPeers = 0 560 // {{end}} 561 } 562 // {{else}} 563 // There is a single partition in the whole input, and 564 // r.numTuplesInPartition already contains the correct number. 565 // {{end}} 566 567 if peersCol[i] { 568 // {{if .IsPercentRank}} 569 r.rank += r.rankIncrement 570 r.rankIncrement = 0 571 // {{end}} 572 // {{if .IsCumeDist}} 573 // We have encountered a new peer group, and we need to update the 574 // number of preceding tuples and get the number of tuples in 575 // this peer group. 576 r.numPrecedingTuples += r.numPeers 577 if r.peerGroupsState.idx == r.peerGroupsState.dequeuedSizes.Length() { 578 if r.peerGroupsState.dequeuedSizes, err = r.peerGroupsState.dequeue(ctx); err != nil { 579 colexecerror.InternalError(err) 580 } 581 r.peerGroupsState.idx = 0 582 } 583 r.numPeers = r.peerGroupsState.dequeuedSizes.ColVec(0).Int64()[r.peerGroupsState.idx] 584 r.peerGroupsState.idx++ 585 // {{end}} 586 } 587 588 // Now we can compute the value of the window function for i'th 589 // tuple. 590 // {{if .IsPercentRank}} 591 if r.numTuplesInPartition == 1 { 592 // There is a single tuple in the partition, so we return 0, per spec. 593 relativeRankOutputCol[i] = 0 594 } else { 595 relativeRankOutputCol[i] = float64(r.rank-1) / float64(r.numTuplesInPartition-1) 596 } 597 r.rankIncrement++ 598 // {{end}} 599 // {{if .IsCumeDist}} 600 relativeRankOutputCol[i] = float64(r.numPrecedingTuples+r.numPeers) / float64(r.numTuplesInPartition) 601 // {{end}} 602 } 603 r.output.SetLength(n) 604 return r.output 605 606 case relativeRankFinished: 607 if err := r.idempotentCloseLocked(ctx); err != nil { 608 colexecerror.InternalError(err) 609 } 610 return coldata.ZeroBatch 611 612 default: 613 colexecerror.InternalError("percent rank operator in unhandled state") 614 // This code is unreachable, but the compiler cannot infer that. 615 return nil 616 } 617 } 618 } 619 620 func (r *_RELATIVE_RANK_STRINGOp) IdempotentClose(ctx context.Context) error { 621 r.mu.Lock() 622 defer r.mu.Unlock() 623 return r.idempotentCloseLocked(ctx) 624 } 625 626 func (r *_RELATIVE_RANK_STRINGOp) idempotentCloseLocked(ctx context.Context) error { 627 if !r.close() { 628 return nil 629 } 630 var lastErr error 631 if err := r.bufferedTuples.close(ctx); err != nil { 632 lastErr = err 633 } 634 // {{if .HasPartition}} 635 if err := r.partitionsState.close(ctx); err != nil { 636 lastErr = err 637 } 638 // {{end}} 639 // {{if .IsCumeDist}} 640 if err := r.peerGroupsState.close(ctx); err != nil { 641 lastErr = err 642 } 643 // {{end}} 644 return lastErr 645 } 646 647 // {{end}}