| 73 | } |
| 74 | |
| 75 | int TopNNode::Heap::InsertTupleWithTieHandling( |
| 76 | const TupleRowComparator& cmp, TopNNode* node, Tuple* materialized_tuple) { |
| 77 | DCHECK(include_ties()); |
| 78 | DCHECK_EQ(capacity_, priority_queue_.Size()) |
| 79 | << "Ties only need special handling when heap is at capacity"; |
| 80 | const TupleDescriptor& tuple_desc = *node->output_tuple_desc_; |
| 81 | Tuple* top_tuple = priority_queue_.Top(); |
| 82 | // If we need to retain ties with the current head, the logic is more complex - we |
| 83 | // have a logical heap in indices [0, heap_capacity()) of 'priority_queue_' plus |
| 84 | // some number of tuples in 'overflowed_ties_' that are equal to priority_queue_.Top() |
| 85 | // according to 'cmp'. |
| 86 | int cmp_result = cmp.Compare(materialized_tuple, top_tuple); |
| 87 | if (cmp_result == 0) { |
| 88 | // This is a duplicate of the current min, we need to include it as a tie with min. |
| 89 | Tuple* insert_tuple = |
| 90 | reinterpret_cast<Tuple*>(node->tuple_pool_->Allocate(node->tuple_byte_size())); |
| 91 | materialized_tuple->DeepCopy(insert_tuple, tuple_desc, node->tuple_pool_.get()); |
| 92 | overflowed_ties_.push_back(insert_tuple); |
| 93 | return 0; |
| 94 | } else if (cmp_result > 0) { |
| 95 | // Tuple does not belong in the heap. |
| 96 | return 1; |
| 97 | } else { |
| 98 | // 'materialized_tuple' needs to be added. Figure out which other tuples, if any, |
| 99 | // need to be removed from the heap. |
| 100 | DCHECK_LT(cmp_result, 0); |
| 101 | // Pop off the head. |
| 102 | priority_queue_.Pop(); |
| 103 | |
| 104 | // Check if 'top_tuple' (the tuple we just popped off) is tied with the new head. |
| 105 | if (heap_capacity() > 1 && cmp.Compare(top_tuple, priority_queue_.Top()) == 0) { |
| 106 | // The new top is still tied with the tuples in 'overflowed_ties_' so we must keep |
| 107 | // it. The previous top becomes another overflowed tuple. |
| 108 | overflowed_ties_.push_back(top_tuple); |
| 109 | Tuple* insert_tuple = |
| 110 | reinterpret_cast<Tuple*>(node->tuple_pool_->Allocate(node->tuple_byte_size())); |
| 111 | materialized_tuple->DeepCopy(insert_tuple, tuple_desc, node->tuple_pool_.get()); |
| 112 | // Push the new tuple onto the heap, but retain the tied tuple. |
| 113 | priority_queue_.Push(insert_tuple); |
| 114 | return 0; |
| 115 | } else { |
| 116 | // No tuples tied with 'top_tuple' are left. |
| 117 | // Reuse the fixed-length memory of 'top_tuple' to reduce allocations. |
| 118 | int64_t num_rows_replaced = overflowed_ties_.size() + 1; |
| 119 | overflowed_ties_.clear(); |
| 120 | materialized_tuple->DeepCopy(top_tuple, tuple_desc, node->tuple_pool_.get()); |
| 121 | priority_queue_.Push(top_tuple); |
| 122 | return num_rows_replaced; |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void TopNNode::InsertBatchPartitioned(RuntimeState* state, RowBatch* batch) { |
| 128 | DCHECK(is_partitioned()); |
nothing calls this directly
no test coverage detected