| 1052 | } // namespace |
| 1053 | |
| 1054 | Result<std::unique_ptr<RecordBatchReader>> FileReaderImpl::GetRecordBatchReader( |
| 1055 | const std::vector<int>& row_groups, const std::vector<int>& column_indices) { |
| 1056 | RETURN_NOT_OK(BoundsCheck(row_groups, column_indices)); |
| 1057 | |
| 1058 | if (reader_properties_.pre_buffer()) { |
| 1059 | // PARQUET-1698/PARQUET-1820: pre-buffer row groups/column chunks if enabled |
| 1060 | BEGIN_PARQUET_CATCH_EXCEPTIONS |
| 1061 | reader_->PreBuffer(row_groups, column_indices, reader_properties_.io_context(), |
| 1062 | reader_properties_.cache_options()); |
| 1063 | END_PARQUET_CATCH_EXCEPTIONS |
| 1064 | } |
| 1065 | |
| 1066 | std::vector<std::shared_ptr<ColumnReaderImpl>> readers; |
| 1067 | std::shared_ptr<::arrow::Schema> batch_schema; |
| 1068 | RETURN_NOT_OK(GetFieldReaders(column_indices, row_groups, &readers, &batch_schema)); |
| 1069 | |
| 1070 | if (readers.empty()) { |
| 1071 | // Just generate all batches right now; they're cheap since they have no columns. |
| 1072 | int64_t batch_size = properties().batch_size(); |
| 1073 | auto max_sized_batch = |
| 1074 | ::arrow::RecordBatch::Make(batch_schema, batch_size, ::arrow::ArrayVector{}); |
| 1075 | |
| 1076 | ::arrow::RecordBatchVector batches; |
| 1077 | |
| 1078 | for (int row_group : row_groups) { |
| 1079 | int64_t num_rows = parquet_reader()->metadata()->RowGroup(row_group)->num_rows(); |
| 1080 | |
| 1081 | batches.insert(batches.end(), static_cast<size_t>(num_rows / batch_size), |
| 1082 | max_sized_batch); |
| 1083 | |
| 1084 | if (int64_t trailing_rows = num_rows % batch_size) { |
| 1085 | batches.push_back(max_sized_batch->Slice(0, trailing_rows)); |
| 1086 | } |
| 1087 | } |
| 1088 | |
| 1089 | return std::make_unique<RowGroupRecordBatchReader>( |
| 1090 | ::arrow::MakeVectorIterator(std::move(batches)), std::move(batch_schema)); |
| 1091 | } |
| 1092 | |
| 1093 | int64_t num_rows = 0; |
| 1094 | for (int row_group : row_groups) { |
| 1095 | num_rows += parquet_reader()->metadata()->RowGroup(row_group)->num_rows(); |
| 1096 | } |
| 1097 | |
| 1098 | using ::arrow::RecordBatchIterator; |
| 1099 | |
| 1100 | // NB: This lambda will be invoked outside the scope of this call to |
| 1101 | // `GetRecordBatchReader()`, so it must capture `readers` and `batch_schema` by value. |
| 1102 | // `this` is a non-owning pointer so we are relying on the parent FileReader outliving |
| 1103 | // this RecordBatchReader. |
| 1104 | ::arrow::Iterator<RecordBatchIterator> batches = ::arrow::MakeFunctionIterator( |
| 1105 | [readers, batch_schema, num_rows, |
| 1106 | this]() mutable -> ::arrow::Result<RecordBatchIterator> { |
| 1107 | ::arrow::ChunkedArrayVector columns(readers.size()); |
| 1108 | |
| 1109 | // don't reserve more rows than necessary |
| 1110 | int64_t batch_size = std::min(properties().batch_size(), num_rows); |
| 1111 | num_rows -= batch_size; |