| 707 | } |
| 708 | |
| 709 | Status BufferedTupleStream::UnpinStream(UnpinMode mode) { |
| 710 | CHECK_CONSISTENCY_FULL(read_it_); |
| 711 | DCHECK(!closed_); |
| 712 | if (mode == UNPIN_ALL) { |
| 713 | // Invalidate the iterators so they don't keep pages pinned. |
| 714 | InvalidateWriteIterator(); |
| 715 | InvalidateReadIterator(); |
| 716 | } |
| 717 | |
| 718 | if (pinned_) { |
| 719 | CHECK_CONSISTENCY_FULL(read_it_); |
| 720 | bool defer_advancing_read_page = false; |
| 721 | if (&*read_it_.read_page_ != write_page_ && read_it_.read_page_ != pages_.end() |
| 722 | && read_it_.read_page_rows_returned_ == read_it_.read_page_->num_rows) { |
| 723 | if (has_write_iterator_ && read_it_.attach_on_read_ |
| 724 | && (num_pages_ <= 2 || !read_it_.read_page_->attached_to_output_batch)) { |
| 725 | // In a read-write stream + attach_on_read mode, there are cases where we should |
| 726 | // NOT advance the read page even though the page has been fully exhausted: |
| 727 | // |
| 728 | // 1. Stream has exactly 2 pages: 1 read and 1 write. |
| 729 | // NextReadPage() will attempt to save default_page_len_ into write |
| 730 | // reservation if the stream ended up with only 1 read/write page after |
| 731 | // advancing the read page. This can potentially lead to negative unused |
| 732 | // reservation if the reader has not freed the row batch where the read page |
| 733 | // buffer is attached to (see IMPALA-10584). |
| 734 | // 2. Read page buffer has not been attached yet to the output row batch. |
| 735 | // The previous GetNext() would not attach the read page buffer to the output |
| 736 | // row batch if it was a read-write page (see IMPALA-10714). |
| 737 | // |
| 738 | // We defer advancing the read page for these cases until the next GetNext() |
| 739 | // call by the reader. |
| 740 | defer_advancing_read_page = true; |
| 741 | } |
| 742 | |
| 743 | if (!defer_advancing_read_page) { |
| 744 | RETURN_IF_ERROR(NextReadPage(&read_it_)); |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | // If the stream was pinned, there may be some remaining pinned pages that should |
| 749 | // be unpinned at this point. |
| 750 | DCHECK_EQ(bytes_unpinned_, 0); |
| 751 | std::list<Page>::iterator it = pages_.begin(); |
| 752 | if (defer_advancing_read_page) { |
| 753 | // We skip advancing the read page earlier, so the first page must be a read page |
| 754 | // and the reader has not done reading it. We should keep the first page pinned. The |
| 755 | // next GetNext() call is the one who will be responsible to unpin the first page. |
| 756 | DCHECK(read_it_.read_page_ == pages_.begin()); |
| 757 | ++it; |
| 758 | } |
| 759 | while (it != pages_.end()) { |
| 760 | UnpinPageIfNeeded(&(*it), false); |
| 761 | ++it; |
| 762 | } |
| 763 | |
| 764 | // Check to see if we need to save some of the reservation we freed up. |
| 765 | if (!NeedWriteReservation(true) && NeedWriteReservation(false)) { |
| 766 | buffer_pool_client_->SaveReservation(&write_page_reservation_, default_page_len_); |