Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.

Commit 176b08c

Browse files
Artem Tarasovwesm
authored andcommitted
PARQUET-593: Add API for writing Page statistics
Author: Artem Tarasov <artem.tarasov@embl.de> Closes #129 from lomereiter/parquet-593 and squashes the following commits: 0be02ec [Artem Tarasov] further cleanup in TypedColumnWriter, fixed tests 6a06dc1 [Artem Tarasov] address reviewer comments a32ccc7 [Artem Tarasov] put back accidentally removed counts printing f42950d [Artem Tarasov] fix subobject initialization 48ff923 [Artem Tarasov] fixed linter warnings f16b121 [Artem Tarasov] switched to signed binary comparison, added tests 2e39fc7 [Artem Tarasov] fix benchmark c26508d [Artem Tarasov] use PrimitiveTypedTest for the test bdf6a2d [Artem Tarasov] writing min/max/null count statistics
1 parent 7abb9c4 commit 176b08c

30 files changed

Lines changed: 1201 additions & 219 deletions

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ set(LIBPARQUET_SRCS
509509
src/parquet/column/writer.cc
510510
src/parquet/column/scanner.cc
511511
src/parquet/column/scan-all.cc
512+
src/parquet/column/statistics.cc
512513

513514
src/parquet/compression/codec.cc
514515
src/parquet/compression/snappy-codec.cc

src/parquet/arrow/arrow-reader-writer-test.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ class TestParquetIO : public ::testing::Test {
258258
typedef ::testing::Types<::arrow::BooleanType, ::arrow::UInt8Type, ::arrow::Int8Type,
259259
::arrow::UInt16Type, ::arrow::Int16Type, ::arrow::Int32Type, ::arrow::UInt64Type,
260260
::arrow::Int64Type, ::arrow::TimestampType, ::arrow::FloatType, ::arrow::DoubleType,
261-
::arrow::StringType>
262-
TestTypes;
261+
::arrow::StringType> TestTypes;
263262

264263
TYPED_TEST_CASE(TestParquetIO, TestTypes);
265264

@@ -477,8 +476,8 @@ class TestPrimitiveParquetIO : public TestParquetIO<TestType> {
477476

478477
typedef ::testing::Types<::arrow::BooleanType, ::arrow::UInt8Type, ::arrow::Int8Type,
479478
::arrow::UInt16Type, ::arrow::Int16Type, ::arrow::UInt32Type, ::arrow::Int32Type,
480-
::arrow::UInt64Type, ::arrow::Int64Type, ::arrow::FloatType, ::arrow::DoubleType>
481-
PrimitiveTestTypes;
479+
::arrow::UInt64Type, ::arrow::Int64Type, ::arrow::FloatType,
480+
::arrow::DoubleType> PrimitiveTestTypes;
482481

483482
TYPED_TEST_CASE(TestPrimitiveParquetIO, PrimitiveTestTypes);
484483

src/parquet/column/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ install(FILES
2424
scan-all.h
2525
scanner.h
2626
writer.h
27+
statistics.h
2728
DESTINATION include/parquet/column)
2829

2930
ADD_PARQUET_TEST(column-reader-test)
3031
ADD_PARQUET_TEST(column-writer-test)
3132
ADD_PARQUET_TEST(levels-test)
3233
ADD_PARQUET_TEST(properties-test)
3334
ADD_PARQUET_TEST(scanner-test)
35+
ADD_PARQUET_TEST(statistics-test)
3436

3537
ADD_PARQUET_BENCHMARK(column-io-benchmark)
3638
ADD_PARQUET_BENCHMARK(level-benchmark)

src/parquet/column/column-io-benchmark.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ std::unique_ptr<Int64Writer> BuildWriter(int64_t output_size, OutputStream* dst,
3535
std::unique_ptr<SerializedPageWriter> pager(
3636
new SerializedPageWriter(dst, Compression::UNCOMPRESSED, metadata));
3737
return std::unique_ptr<Int64Writer>(new Int64Writer(
38-
schema, std::move(pager), output_size, Encoding::PLAIN, properties));
38+
metadata, std::move(pager), output_size, Encoding::PLAIN, properties));
3939
}
4040

4141
std::shared_ptr<ColumnDescriptor> Int64Schema(Repetition::type repetition) {

src/parquet/column/column-writer-test.cc

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,53 +60,62 @@ class TestPrimitiveWriter : public PrimitiveTypedTest<TestType> {
6060

6161
Type::type type_num() { return TestType::type_num; }
6262

63-
void BuildReader() {
63+
void BuildReader(Compression::type compression = Compression::UNCOMPRESSED) {
6464
auto buffer = sink_->GetBuffer();
6565
std::unique_ptr<InMemoryInputStream> source(new InMemoryInputStream(buffer));
6666
std::unique_ptr<SerializedPageReader> page_reader(
67-
new SerializedPageReader(std::move(source), Compression::UNCOMPRESSED));
67+
new SerializedPageReader(std::move(source), compression));
6868
reader_.reset(new TypedColumnReader<TestType>(this->descr_, std::move(page_reader)));
6969
}
7070

7171
std::shared_ptr<TypedColumnWriter<TestType>> BuildWriter(
72-
int64_t output_size = SMALL_SIZE, Encoding::type encoding = Encoding::PLAIN) {
72+
int64_t output_size = SMALL_SIZE,
73+
const ColumnProperties& column_properties = ColumnProperties()) {
7374
sink_.reset(new InMemoryOutputStream());
7475
metadata_ = ColumnChunkMetaDataBuilder::Make(
7576
writer_properties_, this->descr_, reinterpret_cast<uint8_t*>(&thrift_metadata_));
76-
std::unique_ptr<SerializedPageWriter> pager(new SerializedPageWriter(
77-
sink_.get(), Compression::UNCOMPRESSED, metadata_.get()));
77+
std::unique_ptr<SerializedPageWriter> pager(
78+
new SerializedPageWriter(sink_.get(), column_properties.codec, metadata_.get()));
7879
WriterProperties::Builder wp_builder;
79-
if (encoding == Encoding::PLAIN_DICTIONARY || encoding == Encoding::RLE_DICTIONARY) {
80+
if (column_properties.encoding == Encoding::PLAIN_DICTIONARY ||
81+
column_properties.encoding == Encoding::RLE_DICTIONARY) {
8082
wp_builder.enable_dictionary();
8183
} else {
8284
wp_builder.disable_dictionary();
83-
wp_builder.encoding(encoding);
85+
wp_builder.encoding(column_properties.encoding);
8486
}
8587
writer_properties_ = wp_builder.build();
8688
std::shared_ptr<ColumnWriter> writer = ColumnWriter::Make(
87-
this->descr_, std::move(pager), output_size, writer_properties_.get());
89+
metadata_.get(), std::move(pager), output_size, writer_properties_.get());
8890
return std::static_pointer_cast<TypedColumnWriter<TestType>>(writer);
8991
}
9092

91-
void ReadColumn() {
92-
BuildReader();
93+
void ReadColumn(Compression::type compression = Compression::UNCOMPRESSED) {
94+
BuildReader(compression);
9395
reader_->ReadBatch(this->values_out_.size(), definition_levels_out_.data(),
9496
repetition_levels_out_.data(), this->values_out_ptr_, &values_read_);
9597
this->SyncValuesOut();
9698
}
9799

98100
void TestRequiredWithEncoding(Encoding::type encoding) {
101+
return TestRequiredWithSettings(encoding, Compression::UNCOMPRESSED, false, false);
102+
}
103+
104+
void TestRequiredWithSettings(Encoding::type encoding, Compression::type compression,
105+
bool enable_dictionary, bool enable_statistics) {
99106
this->GenerateData(SMALL_SIZE);
100107

101108
// Test case 1: required and non-repeated, so no definition or repetition levels
109+
ColumnProperties column_properties(
110+
encoding, compression, enable_dictionary, enable_statistics);
102111
std::shared_ptr<TypedColumnWriter<TestType>> writer =
103-
this->BuildWriter(SMALL_SIZE, encoding);
112+
this->BuildWriter(SMALL_SIZE, column_properties);
104113
writer->WriteBatch(this->values_.size(), nullptr, nullptr, this->values_ptr_);
105114
// The behaviour should be independent from the number of Close() calls
106115
writer->Close();
107116
writer->Close();
108117

109-
this->ReadColumn();
118+
this->ReadColumn(compression);
110119
ASSERT_EQ(SMALL_SIZE, this->values_read_);
111120
ASSERT_EQ(this->values_, this->values_out_);
112121
}
@@ -115,17 +124,17 @@ class TestPrimitiveWriter : public PrimitiveTypedTest<TestType> {
115124
// Metadata accessor must be created lazily.
116125
// This is because the ColumnChunkMetaData semantics dictate the metadata object is
117126
// complete (no changes to the metadata buffer can be made after instantiation)
118-
auto metadata_accessor =
119-
ColumnChunkMetaData::Make(reinterpret_cast<const uint8_t*>(&thrift_metadata_));
127+
auto metadata_accessor = ColumnChunkMetaData::Make(
128+
reinterpret_cast<const uint8_t*>(&thrift_metadata_), this->descr_);
120129
return metadata_accessor->num_values();
121130
}
122131

123132
std::vector<Encoding::type> metadata_encodings() {
124133
// Metadata accessor must be created lazily.
125134
// This is because the ColumnChunkMetaData semantics dictate the metadata object is
126135
// complete (no changes to the metadata buffer can be made after instantiation)
127-
auto metadata_accessor =
128-
ColumnChunkMetaData::Make(reinterpret_cast<const uint8_t*>(&thrift_metadata_));
136+
auto metadata_accessor = ColumnChunkMetaData::Make(
137+
reinterpret_cast<const uint8_t*>(&thrift_metadata_), this->descr_);
129138
return metadata_accessor->encodings();
130139
}
131140

@@ -148,8 +157,7 @@ class TestPrimitiveWriter : public PrimitiveTypedTest<TestType> {
148157
};
149158

150159
typedef ::testing::Types<Int32Type, Int64Type, Int96Type, FloatType, DoubleType,
151-
BooleanType, ByteArrayType, FLBAType>
152-
TestTypes;
160+
BooleanType, ByteArrayType, FLBAType> TestTypes;
153161

154162
TYPED_TEST_CASE(TestPrimitiveWriter, TestTypes);
155163

@@ -189,6 +197,26 @@ TYPED_TEST(TestPrimitiveWriter, RequiredRLEDictionary) {
189197
}
190198
*/
191199

200+
TYPED_TEST(TestPrimitiveWriter, RequiredPlainWithSnappyCompression) {
201+
this->TestRequiredWithSettings(Encoding::PLAIN, Compression::SNAPPY, false, false);
202+
}
203+
204+
TYPED_TEST(TestPrimitiveWriter, RequiredPlainWithGzipCompression) {
205+
this->TestRequiredWithSettings(Encoding::PLAIN, Compression::GZIP, false, false);
206+
}
207+
208+
TYPED_TEST(TestPrimitiveWriter, RequiredPlainWithStats) {
209+
this->TestRequiredWithSettings(Encoding::PLAIN, Compression::UNCOMPRESSED, false, true);
210+
}
211+
212+
TYPED_TEST(TestPrimitiveWriter, RequiredPlainWithStatsAndSnappyCompression) {
213+
this->TestRequiredWithSettings(Encoding::PLAIN, Compression::SNAPPY, false, true);
214+
}
215+
216+
TYPED_TEST(TestPrimitiveWriter, RequiredPlainWithStatsAndGzipCompression) {
217+
this->TestRequiredWithSettings(Encoding::PLAIN, Compression::GZIP, false, true);
218+
}
219+
192220
TYPED_TEST(TestPrimitiveWriter, Optional) {
193221
// Optional and non-repeated, with definition levels
194222
// but no repetition levels

src/parquet/column/page.h

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <memory>
2727
#include <string>
2828

29+
#include "parquet/column/statistics.h"
2930
#include "parquet/types.h"
3031
#include "parquet/util/buffer.h"
3132

@@ -62,12 +63,14 @@ class DataPage : public Page {
6263
public:
6364
DataPage(const std::shared_ptr<Buffer>& buffer, int32_t num_values,
6465
Encoding::type encoding, Encoding::type definition_level_encoding,
65-
Encoding::type repetition_level_encoding)
66+
Encoding::type repetition_level_encoding,
67+
const EncodedStatistics& statistics = EncodedStatistics())
6668
: Page(buffer, PageType::DATA_PAGE),
6769
num_values_(num_values),
6870
encoding_(encoding),
6971
definition_level_encoding_(definition_level_encoding),
70-
repetition_level_encoding_(repetition_level_encoding) {}
72+
repetition_level_encoding_(repetition_level_encoding),
73+
statistics_(statistics) {}
7174

7275
int32_t num_values() const { return num_values_; }
7376

@@ -77,31 +80,24 @@ class DataPage : public Page {
7780

7881
Encoding::type definition_level_encoding() const { return definition_level_encoding_; }
7982

80-
// DataPageHeader::statistics::max field, if it was set
81-
const uint8_t* max() const { return reinterpret_cast<const uint8_t*>(max_.c_str()); }
82-
83-
// DataPageHeader::statistics::min field, if it was set
84-
const uint8_t* min() const { return reinterpret_cast<const uint8_t*>(min_.c_str()); }
83+
const EncodedStatistics& statistics() const { return statistics_; }
8584

8685
private:
8786
int32_t num_values_;
8887
Encoding::type encoding_;
8988
Encoding::type definition_level_encoding_;
9089
Encoding::type repetition_level_encoding_;
91-
92-
// So max/min can be populated privately
93-
friend class SerializedPageReader;
94-
std::string max_;
95-
std::string min_;
90+
EncodedStatistics statistics_;
9691
};
9792

9893
class CompressedDataPage : public DataPage {
9994
public:
10095
CompressedDataPage(const std::shared_ptr<Buffer>& buffer, int32_t num_values,
10196
Encoding::type encoding, Encoding::type definition_level_encoding,
102-
Encoding::type repetition_level_encoding, int64_t uncompressed_size)
97+
Encoding::type repetition_level_encoding, int64_t uncompressed_size,
98+
const EncodedStatistics& statistics = EncodedStatistics())
10399
: DataPage(buffer, num_values, encoding, definition_level_encoding,
104-
repetition_level_encoding),
100+
repetition_level_encoding, statistics),
105101
uncompressed_size_(uncompressed_size) {}
106102

107103
int64_t uncompressed_size() const { return uncompressed_size_; }

0 commit comments

Comments
 (0)