| 26 | namespace impala { |
| 27 | |
| 28 | Status OutboundRowBatch::AppendRow(const TupleRow* row, const RowDescriptor* row_desc) { |
| 29 | DCHECK(row != nullptr); |
| 30 | int num_tuples = row_desc->num_tuples_no_inline(); |
| 31 | vector<TupleDescriptor*>::const_iterator desc = |
| 32 | row_desc->tuple_descriptors().begin(); |
| 33 | for (int j = 0; j < num_tuples; ++desc, ++j) { |
| 34 | Tuple* tuple = row->GetTuple(j); |
| 35 | if (UNLIKELY(tuple == nullptr)) { |
| 36 | // NULLs are encoded as -1 |
| 37 | tuple_offsets_.push_back(-1); |
| 38 | continue; |
| 39 | } |
| 40 | // Record offset before creating copy (which increments offset and tuple_data) |
| 41 | tuple_offsets_.push_back(tuple_data_offset_); |
| 42 | // Try appending tuple to current tuple_data_. If it doesn't fit to the buffer, |
| 43 | // get the exact size needed for the tuple and allocate enough memory for it. |
| 44 | // This allows iterating through the varlen slots of most tuples only once. |
| 45 | if (UNLIKELY(!TryAppendTuple(tuple, *desc))) { |
| 46 | int64_t tuple_size = tuple->TotalByteSize(**desc, true /*assume_smallify*/); |
| 47 | int64_t new_size = tuple_data_offset_ + tuple_size; |
| 48 | if (new_size > numeric_limits<int32_t>::max()) { |
| 49 | return Status( |
| 50 | TErrorCode::ROW_BATCH_TOO_LARGE, new_size, numeric_limits<int32_t>::max()); |
| 51 | } |
| 52 | // TODO: Based on experience the below logic doubles the buffer size instead of |
| 53 | // resizing to the exact size, similarly to vector. It would be clearer to use a |
| 54 | // vector instead of string for tuple_data_, but in the long term it would be |
| 55 | // better to use a fixed sized buffer (data_stream_sender_buffer_size) once var |
| 56 | // len data is properly accounted for (see IMPALA-12594 for details). |
| 57 | tuple_data_.resize(new_size); |
| 58 | tuple_data_.resize(tuple_data_.capacity()); |
| 59 | DCHECK_GT(tuple_data_.size(), 0); |
| 60 | bool retry_successful = TryAppendTuple(tuple, *desc); |
| 61 | // As the buffer was resized based on the exact size of the tuple the second |
| 62 | // attempt must succeed. |
| 63 | DCHECK(retry_successful); |
| 64 | } |
| 65 | DCHECK_LE(tuple_data_offset_, tuple_data_.size()); |
| 66 | } |
| 67 | return Status::OK(); |
| 68 | } |
| 69 | |
| 70 | bool OutboundRowBatch::TryAppendTuple(const Tuple* tuple, const TupleDescriptor* desc) { |
| 71 | DCHECK(tuple != nullptr); |
no test coverage detected