| 511 | } |
| 512 | |
| 513 | inline Status AnalyticEvalNode::InitNextPartition(RuntimeState* state, |
| 514 | int64_t stream_idx) { |
| 515 | VLOG_FILE << id() << " InitNextPartition idx=" << stream_idx; |
| 516 | DCHECK_LT(curr_partition_idx_, stream_idx); |
| 517 | int64_t prev_partition_stream_idx = curr_partition_idx_; |
| 518 | curr_partition_idx_ = stream_idx; |
| 519 | |
| 520 | // If the window has an end bound preceding the current row, we will have output tuples |
| 521 | // for rows beyond the previous partition, so they should be removed. Because |
| 522 | // result_tuples_ is a sparse structure, the last result tuple of the previous |
| 523 | // partition may have been added to result_tuples_ with a stream index equal to or |
| 524 | // beyond curr_partition_idx_. So the last entry in result_tuples_ with a stream index |
| 525 | // >= curr_partition_idx_ is the last result tuple of the previous partition. Adding |
| 526 | // the last result tuple to result_tuples_ with a stream index curr_partition_idx_ - 1 |
| 527 | // ensures that all rows in the previous partition have corresponding analytic results. |
| 528 | Tuple* prev_partition_last_result_tuple = nullptr; |
| 529 | while (!result_tuples_.empty() && result_tuples_.back().first >= curr_partition_idx_) { |
| 530 | DCHECK(fn_scope_ == ROWS && window_.__isset.window_end && |
| 531 | window_.window_end.type == TAnalyticWindowBoundaryType::PRECEDING); |
| 532 | VLOG_ROW << id() << " Removing result past partition idx: " |
| 533 | << result_tuples_.back().first; |
| 534 | prev_partition_last_result_tuple = result_tuples_.back().second; |
| 535 | result_tuples_.pop_back(); |
| 536 | } |
| 537 | if (prev_partition_last_result_tuple != nullptr) { |
| 538 | if (result_tuples_.empty() || |
| 539 | result_tuples_.back().first < curr_partition_idx_ - 1) { |
| 540 | // prev_partition_last_result_tuple was the last result tuple in the partition, add |
| 541 | // it back with the index of the last row in the partition so that all output rows |
| 542 | // in this partition get the correct value. |
| 543 | result_tuples_.emplace_back( |
| 544 | curr_partition_idx_ - 1, prev_partition_last_result_tuple); |
| 545 | } |
| 546 | DCHECK(!result_tuples_.empty()); |
| 547 | last_result_idx_ = result_tuples_.back().first; |
| 548 | VLOG_ROW << id() << " After removing results past partition: " |
| 549 | << DebugStateString(true); |
| 550 | DCHECK_EQ(last_result_idx_, curr_partition_idx_ - 1); |
| 551 | DCHECK_LE(input_stream_->rows_returned(), last_result_idx_); |
| 552 | } |
| 553 | DCHECK(result_tuples_.empty() || (last_result_idx_ == result_tuples_.back().first)); |
| 554 | |
| 555 | if (fn_scope_ == ROWS && stream_idx > 0 && (!window_.__isset.window_end || |
| 556 | window_.window_end.type == TAnalyticWindowBoundaryType::FOLLOWING)) { |
| 557 | RETURN_IF_ERROR(TryAddRemainingResults(stream_idx, prev_partition_stream_idx)); |
| 558 | } |
| 559 | window_tuples_.clear(); |
| 560 | |
| 561 | VLOG_ROW << id() << " Reset curr_tuple"; |
| 562 | // Call finalize to release resources; result is not needed but the dst tuple must be |
| 563 | // a tuple described by result_tuple_desc_. |
| 564 | DCHECK(curr_tuple_init_); |
| 565 | AggFnEvaluator::Finalize(analytic_fn_evals_, curr_tuple_, dummy_result_tuple_); |
| 566 | // Re-initialize curr_tuple_. |
| 567 | // TODO: zeroing out curr_tuple_ shouldn't be strictly necessary. |
| 568 | curr_tuple_->Init(intermediate_tuple_desc_->byte_size()); |
| 569 | AggFnEvaluator::Init(analytic_fn_evals_, curr_tuple_); |
| 570 | // Check for errors in AggFnEvaluator::Init(). |
nothing calls this directly
no test coverage detected