| 3020 | } |
| 3021 | |
| 3022 | void GetReadRecordBatchReadRanges( |
| 3023 | uint32_t num_rows, const std::vector<int>& included_fields, |
| 3024 | const std::vector<int64_t>& expected_body_read_lengths) { |
| 3025 | auto buffer = MakeBooleanInt32Int64File(num_rows, /*num_batches=*/1); |
| 3026 | |
| 3027 | io::BufferReader buffer_reader(buffer); |
| 3028 | std::unique_ptr<io::TrackedRandomAccessFile> tracked = |
| 3029 | io::TrackedRandomAccessFile::Make(&buffer_reader); |
| 3030 | |
| 3031 | auto read_options = IpcReadOptions::Defaults(); |
| 3032 | // if empty, return all fields |
| 3033 | read_options.included_fields = included_fields; |
| 3034 | ASSERT_OK_AND_ASSIGN(auto reader, |
| 3035 | RecordBatchFileReader::Open(tracked.get(), read_options)); |
| 3036 | ASSERT_OK_AND_ASSIGN(auto out_batch, reader->ReadRecordBatch(0)); |
| 3037 | |
| 3038 | ASSERT_EQ(out_batch->num_rows(), num_rows); |
| 3039 | ASSERT_EQ(out_batch->num_columns(), |
| 3040 | included_fields.empty() ? 3 : included_fields.size()); |
| 3041 | |
| 3042 | auto read_ranges = tracked->get_read_ranges(); |
| 3043 | |
| 3044 | const int32_t magic_size = static_cast<int>(ipc::internal::kArrowMagicBytes.size()); |
| 3045 | // read magic and footer length IO |
| 3046 | auto file_end_size = magic_size + sizeof(int32_t); |
| 3047 | auto footer_length_offset = buffer->size() - file_end_size; |
| 3048 | auto footer_length = bit_util::FromLittleEndian( |
| 3049 | util::SafeLoadAs<int32_t>(buffer->data() + footer_length_offset)); |
| 3050 | |
| 3051 | // there are at least 2 read IOs before reading body: |
| 3052 | // 1) read magic and footer length IO |
| 3053 | // 2) footer IO |
| 3054 | EXPECT_GE(read_ranges.size(), 2); |
| 3055 | |
| 3056 | // read magic and footer length IO |
| 3057 | EXPECT_EQ(read_ranges[0].length, file_end_size); |
| 3058 | // read footer IO |
| 3059 | EXPECT_EQ(read_ranges[1].length, footer_length); |
| 3060 | |
| 3061 | if (included_fields.empty()) { |
| 3062 | // When no fields are explicitly included, the reader optimizes by |
| 3063 | // reading metadata and the entire body in a single IO. |
| 3064 | // Thus, there are exactly 3 read IOs in total: |
| 3065 | // 1) magic and footer length |
| 3066 | // 2) footer |
| 3067 | // 3) record batch metadata + body |
| 3068 | EXPECT_EQ(read_ranges.size(), 3); |
| 3069 | |
| 3070 | int64_t total_body = 0; |
| 3071 | for (auto len : expected_body_read_lengths) total_body += len; |
| 3072 | |
| 3073 | // In the optimized path (included_fields is empty), the 3rd read operation |
| 3074 | // fetches both the message metadata (flatbuffer) and the entire message body |
| 3075 | // in one contiguous block. Therefore, its length must at least exceed the |
| 3076 | // total body length by the size of the metadata. |
| 3077 | EXPECT_GT(read_ranges[2].length, total_body); |
| 3078 | EXPECT_LE(read_ranges[2].length, total_body + footer_length); |
| 3079 | } else { |
no test coverage detected