| 1003 | } // namespace |
| 1004 | |
| 1005 | Result<std::unique_ptr<RecordBatchReader>> FileReaderImpl::GetRecordBatchReader( |
| 1006 | const std::vector<int>& row_groups, const std::vector<int>& column_indices) { |
| 1007 | RETURN_NOT_OK(BoundsCheck(row_groups, column_indices)); |
| 1008 | |
| 1009 | if (reader_properties_.pre_buffer()) { |
| 1010 | // PARQUET-1698/PARQUET-1820: pre-buffer row groups/column chunks if enabled |
| 1011 | BEGIN_PARQUET_CATCH_EXCEPTIONS |
| 1012 | reader_->PreBuffer(row_groups, column_indices, reader_properties_.io_context(), |
| 1013 | reader_properties_.cache_options()); |
| 1014 | END_PARQUET_CATCH_EXCEPTIONS |
| 1015 | } |
| 1016 | |
| 1017 | std::vector<std::shared_ptr<ColumnReaderImpl>> readers; |
| 1018 | std::shared_ptr<::arrow::Schema> batch_schema; |
| 1019 | RETURN_NOT_OK(GetFieldReaders(column_indices, row_groups, &readers, &batch_schema)); |
| 1020 | |
| 1021 | if (readers.empty()) { |
| 1022 | // Just generate all batches right now; they're cheap since they have no columns. |
| 1023 | int64_t batch_size = properties().batch_size(); |
| 1024 | auto max_sized_batch = |
| 1025 | ::arrow::RecordBatch::Make(batch_schema, batch_size, ::arrow::ArrayVector{}); |
| 1026 | |
| 1027 | ::arrow::RecordBatchVector batches; |
| 1028 | |
| 1029 | for (int row_group : row_groups) { |
| 1030 | int64_t num_rows = parquet_reader()->metadata()->RowGroup(row_group)->num_rows(); |
| 1031 | |
| 1032 | batches.insert(batches.end(), static_cast<size_t>(num_rows / batch_size), |
| 1033 | max_sized_batch); |
| 1034 | |
| 1035 | if (int64_t trailing_rows = num_rows % batch_size) { |
| 1036 | batches.push_back(max_sized_batch->Slice(0, trailing_rows)); |
| 1037 | } |
| 1038 | } |
| 1039 | |
| 1040 | return std::make_unique<RowGroupRecordBatchReader>( |
| 1041 | ::arrow::MakeVectorIterator(std::move(batches)), std::move(batch_schema)); |
| 1042 | } |
| 1043 | |
| 1044 | int64_t num_rows = 0; |
| 1045 | for (int row_group : row_groups) { |
| 1046 | num_rows += parquet_reader()->metadata()->RowGroup(row_group)->num_rows(); |
| 1047 | } |
| 1048 | |
| 1049 | using ::arrow::RecordBatchIterator; |
| 1050 | |
| 1051 | // NB: This lambda will be invoked outside the scope of this call to |
| 1052 | // `GetRecordBatchReader()`, so it must capture `readers` and `batch_schema` by value. |
| 1053 | // `this` is a non-owning pointer so we are relying on the parent FileReader outliving |
| 1054 | // this RecordBatchReader. |
| 1055 | ::arrow::Iterator<RecordBatchIterator> batches = ::arrow::MakeFunctionIterator( |
| 1056 | [readers, batch_schema, num_rows, |
| 1057 | this]() mutable -> ::arrow::Result<RecordBatchIterator> { |
| 1058 | ::arrow::ChunkedArrayVector columns(readers.size()); |
| 1059 | |
| 1060 | // don't reserve more rows than necessary |
| 1061 | int64_t batch_size = std::min(properties().batch_size(), num_rows); |
| 1062 | num_rows -= batch_size; |