| 420 | } |
| 421 | |
| 422 | void TestFullRoundtrip(int64_t num_values, int64_t null_count) { |
| 423 | this->GenerateData(num_values); |
| 424 | |
| 425 | // compute statistics for the whole batch |
| 426 | auto expected_stats = MakeStatistics<TestType>(this->schema_.Column(0)); |
| 427 | expected_stats->Update(this->values_ptr_, num_values - null_count, null_count); |
| 428 | |
| 429 | auto sink = CreateOutputStream(); |
| 430 | auto gnode = std::static_pointer_cast<GroupNode>(this->node_); |
| 431 | std::shared_ptr<WriterProperties> writer_properties = |
| 432 | WriterProperties::Builder().enable_statistics("column")->build(); |
| 433 | auto file_writer = ParquetFileWriter::Open(sink, gnode, writer_properties); |
| 434 | auto row_group_writer = file_writer->AppendRowGroup(); |
| 435 | auto column_writer = |
| 436 | static_cast<TypedColumnWriter<TestType>*>(row_group_writer->NextColumn()); |
| 437 | |
| 438 | // simulate the case when data comes from multiple buffers, |
| 439 | // in which case special care is necessary for FLBA/ByteArray types |
| 440 | for (int i = 0; i < 2; i++) { |
| 441 | int64_t batch_num_values = i ? num_values - num_values / 2 : num_values / 2; |
| 442 | int64_t batch_null_count = i ? null_count : 0; |
| 443 | DCHECK(null_count <= num_values); // avoid too much headache |
| 444 | std::vector<int16_t> definition_levels(batch_null_count, 0); |
| 445 | definition_levels.insert(definition_levels.end(), |
| 446 | batch_num_values - batch_null_count, 1); |
| 447 | auto beg = this->values_.begin() + i * num_values / 2; |
| 448 | auto end = beg + batch_num_values; |
| 449 | std::vector<c_type> batch = GetDeepCopy(std::vector<c_type>(beg, end)); |
| 450 | c_type* batch_values_ptr = GetValuesPointer(batch); |
| 451 | column_writer->WriteBatch(batch_num_values, definition_levels.data(), nullptr, |
| 452 | batch_values_ptr); |
| 453 | DeepFree(batch); |
| 454 | } |
| 455 | column_writer->Close(); |
| 456 | row_group_writer->Close(); |
| 457 | file_writer->Close(); |
| 458 | |
| 459 | ASSERT_OK_AND_ASSIGN(auto buffer, sink->Finish()); |
| 460 | auto source = std::make_shared<::arrow::io::BufferReader>(buffer); |
| 461 | auto file_reader = ParquetFileReader::Open(source); |
| 462 | auto rg_reader = file_reader->RowGroup(0); |
| 463 | auto column_chunk = rg_reader->metadata()->ColumnChunk(0); |
| 464 | if (!column_chunk->is_stats_set()) return; |
| 465 | std::shared_ptr<Statistics> stats = column_chunk->statistics(); |
| 466 | // check values after serialization + deserialization |
| 467 | EXPECT_EQ(null_count, stats->null_count()); |
| 468 | EXPECT_EQ(num_values - null_count, stats->num_values()); |
| 469 | EXPECT_TRUE(expected_stats->HasMinMax()); |
| 470 | EXPECT_EQ(expected_stats->EncodeMin(), stats->EncodeMin()); |
| 471 | EXPECT_EQ(expected_stats->EncodeMax(), stats->EncodeMax()); |
| 472 | |
| 473 | std::shared_ptr<EncodedStatistics> enc_stats = column_chunk->encoded_statistics(); |
| 474 | EXPECT_EQ(null_count, enc_stats->null_count); |
| 475 | EXPECT_TRUE(enc_stats->has_min); |
| 476 | EXPECT_TRUE(enc_stats->has_max); |
| 477 | EXPECT_EQ(expected_stats->EncodeMin(), enc_stats->min()); |
| 478 | EXPECT_EQ(expected_stats->EncodeMax(), enc_stats->max()); |
| 479 | EXPECT_EQ(enc_stats->is_min_value_exact, std::make_optional(true)); |
no test coverage detected