Skip to content

Commit 439103e

Browse files
committed
Implement basic parquet file rewriter
1 parent fb0bac6 commit 439103e

12 files changed

Lines changed: 841 additions & 8 deletions

cpp/src/parquet/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ set(PARQUET_SRCS
173173
encryption/internal_file_encryptor.cc
174174
exception.cc
175175
file_reader.cc
176+
file_rewriter.cc
176177
file_writer.cc
177178
geospatial/statistics.cc
178179
geospatial/util_internal.cc
@@ -412,6 +413,8 @@ add_parquet_test(arrow-reader-writer-test
412413

413414
add_parquet_test(arrow-index-test SOURCES arrow/index_test.cc)
414415

416+
add_parquet_test(arrow-rewriter-test SOURCES arrow/arrow_rewriter_test.cc)
417+
415418
add_parquet_test(arrow-internals-test SOURCES arrow/path_internal_test.cc
416419
arrow/reconstruct_internal_test.cc)
417420

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#include "arrow/io/memory.h"
19+
#include "arrow/testing/gtest_util.h"
20+
#include "parquet/arrow/reader.h"
21+
#include "parquet/file_reader.h"
22+
#include "parquet/file_rewriter.h"
23+
#ifdef _MSC_VER
24+
# pragma warning(push)
25+
// Disable forcing value to bool warnings
26+
# pragma warning(disable : 4800)
27+
#endif
28+
29+
#include <memory>
30+
31+
#include "gtest/gtest.h"
32+
33+
#include "parquet/arrow/test_util.h"
34+
#include "parquet/platform.h"
35+
#include "parquet/properties.h"
36+
37+
using arrow::Table;
38+
using arrow::io::BufferReader;
39+
40+
namespace parquet::arrow {
41+
42+
TEST(ParquetRewriterTest, SimpleRoundTrip) {
43+
auto rewriter_properties =
44+
RewriterProperties::Builder()
45+
.writer_properties(
46+
WriterProperties::Builder().enable_write_page_index()->build())
47+
->build();
48+
49+
auto schema = ::arrow::schema(
50+
{::arrow::field("a", ::arrow::int32()), ::arrow::field("b", ::arrow::utf8())});
51+
52+
std::shared_ptr<Buffer> buffer;
53+
54+
WriteFile(rewriter_properties->writer_properties(),
55+
::arrow::TableFromJSON(schema, {R"([[1, "a"], [2, "b"]])"}), buffer);
56+
57+
auto sink = CreateOutputStream();
58+
auto rewriter =
59+
ParquetFileRewriter::Open({std::make_shared<BufferReader>(buffer)}, sink, {NULLPTR},
60+
NULLPTR, rewriter_properties);
61+
rewriter->Rewrite();
62+
rewriter->Close();
63+
64+
ASSERT_OK_AND_ASSIGN(auto out_buffer, sink->Finish());
65+
auto file_reader = ParquetFileReader::Open(std::make_shared<BufferReader>(out_buffer));
66+
ASSERT_OK_AND_ASSIGN(auto reader, FileReader::Make(::arrow::default_memory_pool(),
67+
std::move(file_reader)));
68+
69+
std::shared_ptr<Table> table;
70+
ASSERT_OK(reader->ReadTable(&table));
71+
ASSERT_OK(table->ValidateFull());
72+
73+
auto expected_table = ::arrow::TableFromJSON(schema, {R"([[1, "a"], [2, "b"]])"});
74+
AssertTablesEqual(*expected_table, *table);
75+
}
76+
77+
TEST(ParquetRewriterTest, ConcatRoundTrip) {
78+
auto rewriter_properties =
79+
RewriterProperties::Builder()
80+
.writer_properties(
81+
WriterProperties::Builder().enable_write_page_index()->build())
82+
->build();
83+
84+
auto schema = ::arrow::schema(
85+
{::arrow::field("a", ::arrow::int32()), ::arrow::field("b", ::arrow::utf8())});
86+
87+
std::shared_ptr<Buffer> buffer_up;
88+
std::shared_ptr<Buffer> buffer_down;
89+
90+
WriteFile(rewriter_properties->writer_properties(),
91+
::arrow::TableFromJSON(schema, {R"([[1, "a"], [2, "b"]])"}), buffer_up);
92+
WriteFile(rewriter_properties->writer_properties(),
93+
::arrow::TableFromJSON(schema, {R"([[3, "c"]])"}), buffer_down);
94+
95+
auto sink = CreateOutputStream();
96+
auto rewriter =
97+
ParquetFileRewriter::Open({std::make_shared<BufferReader>(buffer_up),
98+
std::make_shared<BufferReader>(buffer_down)},
99+
sink, {NULLPTR, NULLPTR}, NULLPTR, rewriter_properties);
100+
rewriter->Rewrite();
101+
rewriter->Close();
102+
103+
ASSERT_OK_AND_ASSIGN(auto out_buffer, sink->Finish());
104+
auto file_reader = ParquetFileReader::Open(std::make_shared<BufferReader>(out_buffer));
105+
ASSERT_OK_AND_ASSIGN(auto reader, FileReader::Make(::arrow::default_memory_pool(),
106+
std::move(file_reader)));
107+
108+
std::shared_ptr<Table> table;
109+
ASSERT_OK(reader->ReadTable(&table));
110+
ASSERT_OK(table->ValidateFull());
111+
112+
auto expected_table =
113+
::arrow::TableFromJSON(schema, {R"([[1, "a"], [2, "b"], [3, "c"]])"});
114+
AssertTablesEqual(*expected_table, *table);
115+
}
116+
117+
} // namespace parquet::arrow

cpp/src/parquet/arrow/test_util.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,23 @@
2828
#include "arrow/array/builder_binary.h"
2929
#include "arrow/array/builder_decimal.h"
3030
#include "arrow/array/builder_primitive.h"
31+
#include "arrow/table.h"
3132
#include "arrow/testing/gtest_util.h"
3233
#include "arrow/testing/random.h"
3334
#include "arrow/type_fwd.h"
3435
#include "arrow/type_traits.h"
3536
#include "arrow/util/decimal.h"
3637
#include "arrow/util/float16.h"
38+
#include "parquet/arrow/schema.h"
39+
#include "parquet/arrow/writer.h"
3740
#include "parquet/column_reader.h"
41+
#include "parquet/file_writer.h"
3842
#include "parquet/test_util.h"
3943

4044
namespace parquet {
4145

4246
using internal::RecordReader;
47+
using schema::GroupNode;
4348

4449
namespace arrow {
4550

@@ -482,6 +487,29 @@ void ExpectArrayT<::arrow::BooleanType>(void* expected, Array* result) {
482487
EXPECT_TRUE(result->Equals(*expected_array));
483488
}
484489

490+
void WriteFile(const std::shared_ptr<WriterProperties>& writer_properties,
491+
const std::shared_ptr<::arrow::Table>& table,
492+
std::shared_ptr<Buffer>& buffer) {
493+
// Get schema from table.
494+
auto schema = table->schema();
495+
std::shared_ptr<SchemaDescriptor> parquet_schema;
496+
auto arrow_writer_properties = default_arrow_writer_properties();
497+
ASSERT_OK_NO_THROW(ToParquetSchema(schema.get(), *writer_properties,
498+
*arrow_writer_properties, &parquet_schema));
499+
auto schema_node = std::static_pointer_cast<GroupNode>(parquet_schema->schema_root());
500+
501+
// Write table to buffer.
502+
auto sink = CreateOutputStream();
503+
auto pool = ::arrow::default_memory_pool();
504+
auto writer = ParquetFileWriter::Open(sink, schema_node, writer_properties);
505+
std::unique_ptr<FileWriter> arrow_writer;
506+
ASSERT_OK(FileWriter::Make(pool, std::move(writer), schema, arrow_writer_properties,
507+
&arrow_writer));
508+
ASSERT_OK_NO_THROW(arrow_writer->WriteTable(*table));
509+
ASSERT_OK_NO_THROW(arrow_writer->Close());
510+
ASSERT_OK_AND_ASSIGN(buffer, sink->Finish());
511+
}
512+
485513
} // namespace arrow
486514

487515
} // namespace parquet

0 commit comments

Comments
 (0)