mirror of
https://github.com/apache/arrow.git
synced 2026-10-28 16:47:19 +08:00
Refactor to link against parquet_arrow, fix up cmake files
Change-Id: Iccbc4080d8a6c83df7f6a110afaab29ae677aba8
This commit is contained in:
1 parent
7e39747eec
commit
d67b4f9d77
21 files changed
+55
-2851
No files matched your search
@@ -52,10 +52,6 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
"Build the libarrow shared libraries"
|
||||
ON)
|
||||
|
||||
option(ARROW_PARQUET
|
||||
"Build the Parquet adapter and link to libparquet"
|
||||
OFF)
|
||||
|
||||
option(ARROW_TEST_MEMCHECK
|
||||
"Run the test suite using valgrind --tool=memcheck"
|
||||
OFF)
|
||||
@@ -702,20 +698,6 @@ add_subdirectory(src/arrow/io)
|
||||
add_subdirectory(src/arrow/util)
|
||||
add_subdirectory(src/arrow/types)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Parquet adapter library
|
||||
|
||||
if(ARROW_PARQUET)
|
||||
find_package(Parquet REQUIRED)
|
||||
include_directories(SYSTEM ${PARQUET_INCLUDE_DIR})
|
||||
ADD_THIRDPARTY_LIB(parquet
|
||||
STATIC_LIB ${PARQUET_STATIC_LIB}
|
||||
SHARED_LIB ${PARQUET_SHARED_LIB})
|
||||
|
||||
add_subdirectory(src/arrow/parquet)
|
||||
list(APPEND LINK_LIBS arrow_parquet parquet)
|
||||
endif()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# IPC library
|
||||
|
||||
|
||||
@@ -29,15 +29,20 @@ endif()
|
||||
|
||||
# Try the parameterized roots, if they exist
|
||||
if ( _parquet_roots )
|
||||
find_path( PARQUET_INCLUDE_DIR NAMES parquet/api/reader.h
|
||||
PATHS ${_parquet_roots} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "include" )
|
||||
find_library( PARQUET_LIBRARIES NAMES parquet
|
||||
PATHS ${_parquet_roots} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "lib" )
|
||||
find_path( PARQUET_INCLUDE_DIR NAMES parquet/api/reader.h
|
||||
PATHS ${_parquet_roots} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "include" )
|
||||
find_library( PARQUET_LIBRARIES NAMES parquet
|
||||
PATHS ${_parquet_roots} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "lib" )
|
||||
|
||||
find_library(PARQUET_ARROW_LIBRARIES NAMES parquet_arrow
|
||||
PATHS ${_parquet_roots} NO_DEFAULT_PATH
|
||||
PATH_SUFFIXES "lib")
|
||||
else ()
|
||||
find_path( PARQUET_INCLUDE_DIR NAMES parquet/api/reader.h )
|
||||
find_library( PARQUET_LIBRARIES NAMES parquet )
|
||||
find_path(PARQUET_INCLUDE_DIR NAMES parquet/api/reader.h )
|
||||
find_library(PARQUET_LIBRARIES NAMES parquet)
|
||||
find_library(PARQUET_ARROW_LIBRARIES NAMES parquet_arrow)
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -51,6 +56,18 @@ else ()
|
||||
set(PARQUET_FOUND FALSE)
|
||||
endif ()
|
||||
|
||||
if (PARQUET_INCLUDE_DIR AND PARQUET_ARROW_LIBRARIES)
|
||||
set(PARQUET_ARROW_FOUND TRUE)
|
||||
get_filename_component(PARQUET_ARROW_LIBS ${PARQUET_ARROW_LIBRARIES} PATH)
|
||||
set(PARQUET_ARROW_LIB_NAME libparquet_arrow)
|
||||
set(PARQUET_ARROW_STATIC_LIB
|
||||
${PARQUET_ARROW_LIBS}/${PARQUET_ARROW_LIB_NAME}.a)
|
||||
set(PARQUET_ARROW_SHARED_LIB
|
||||
${PARQUET_ARROW_LIBS}/${PARQUET_ARROW_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
else ()
|
||||
set(PARQUET_ARROW_FOUND FALSE)
|
||||
endif ()
|
||||
|
||||
if (PARQUET_FOUND)
|
||||
if (NOT Parquet_FIND_QUIETLY)
|
||||
message(STATUS "Found the Parquet library: ${PARQUET_LIBRARIES}")
|
||||
@@ -71,6 +88,12 @@ else ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (PARQUET_ARROW_FOUND)
|
||||
if (NOT Parquet_FIND_QUIETLY)
|
||||
message(STATUS "Found the Parquet Arrow library: ${PARQUET_ARROW_LIBS}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
PARQUET_FOUND
|
||||
PARQUET_INCLUDE_DIR
|
||||
@@ -78,4 +101,9 @@ mark_as_advanced(
|
||||
PARQUET_LIBRARIES
|
||||
PARQUET_STATIC_LIB
|
||||
PARQUET_SHARED_LIB
|
||||
|
||||
PARQUET_ARROW_FOUND
|
||||
PARQUET_ARROW_LIBS
|
||||
PARQUET_ARROW_STATIC_LIB
|
||||
PARQUET_ARROW_SHARED_LIB
|
||||
)
|
||||
+5
-10
@@ -1,24 +1,19 @@
|
||||
## Building Arrow-Parquet integration
|
||||
|
||||
To build the Arrow C++'s Parquet adapter library, you must first build [parquet-cpp][1]:
|
||||
To use Arrow C++ with Parquet, you must first build the Arrow C++ libraries and
|
||||
install them someplace. Then, you can build [parquet-cpp][1] with the Arrow
|
||||
adapter library:
|
||||
|
||||
```bash
|
||||
# Set this to your preferred install location
|
||||
export PARQUET_HOME=$HOME/local
|
||||
export ARROW_HOME=$HOME/local
|
||||
|
||||
git clone https://github.com/apache/parquet-cpp.git
|
||||
cd parquet-cpp
|
||||
source setup_build_env.sh
|
||||
cmake -DCMAKE_INSTALL_PREFIX=$PARQUET_HOME
|
||||
cmake -DCMAKE_INSTALL_PREFIX=$PARQUET_HOME -DPARQUET_ARROW=on
|
||||
make -j4
|
||||
make install
|
||||
```
|
||||
|
||||
Make sure that `$PARQUET_HOME` is set to the installation location. Now, build
|
||||
Arrow with the Parquet adapter enabled:
|
||||
|
||||
```bash
|
||||
cmake -DARROW_PARQUET=ON
|
||||
```
|
||||
|
||||
[1]: https://github.com/apache/parquet-cpp
|
||||
@@ -1,67 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# arrow_parquet : Arrow <-> Parquet adapter
|
||||
|
||||
set(PARQUET_SRCS
|
||||
io.cc
|
||||
reader.cc
|
||||
schema.cc
|
||||
writer.cc
|
||||
)
|
||||
|
||||
set(PARQUET_LIBS
|
||||
arrow_shared
|
||||
arrow_io
|
||||
parquet_shared
|
||||
)
|
||||
|
||||
add_library(arrow_parquet SHARED
|
||||
${PARQUET_SRCS}
|
||||
)
|
||||
target_link_libraries(arrow_parquet ${PARQUET_LIBS})
|
||||
SET_TARGET_PROPERTIES(arrow_parquet PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(arrow_parquet
|
||||
PROPERTIES
|
||||
BUILD_WITH_INSTALL_RPATH ON
|
||||
INSTALL_NAME_DIR "@rpath")
|
||||
endif()
|
||||
|
||||
ADD_ARROW_TEST(parquet-schema-test)
|
||||
ARROW_TEST_LINK_LIBRARIES(parquet-schema-test arrow_parquet)
|
||||
|
||||
ADD_ARROW_TEST(parquet-io-test)
|
||||
ARROW_TEST_LINK_LIBRARIES(parquet-io-test arrow_parquet)
|
||||
|
||||
ADD_ARROW_TEST(parquet-reader-writer-test)
|
||||
ARROW_TEST_LINK_LIBRARIES(parquet-reader-writer-test arrow_parquet)
|
||||
|
||||
# Headers: top level
|
||||
install(FILES
|
||||
io.h
|
||||
reader.h
|
||||
schema.h
|
||||
utils.h
|
||||
writer.h
|
||||
DESTINATION include/arrow/parquet)
|
||||
|
||||
install(TARGETS arrow_parquet
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
@@ -1,105 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include "arrow/parquet/io.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "parquet/api/io.h"
|
||||
|
||||
#include "arrow/parquet/utils.h"
|
||||
#include "arrow/util/memory-pool.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
// To assist with readability
|
||||
using ArrowROFile = arrow::io::ReadableFileInterface;
|
||||
|
||||
namespace arrow {
|
||||
namespace parquet {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ParquetAllocator
|
||||
|
||||
ParquetAllocator::ParquetAllocator() : pool_(default_memory_pool()) {}
|
||||
|
||||
ParquetAllocator::ParquetAllocator(MemoryPool* pool) : pool_(pool) {}
|
||||
|
||||
ParquetAllocator::~ParquetAllocator() {}
|
||||
|
||||
uint8_t* ParquetAllocator::Malloc(int64_t size) {
|
||||
uint8_t* result;
|
||||
PARQUET_THROW_NOT_OK(pool_->Allocate(size, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void ParquetAllocator::Free(uint8_t* buffer, int64_t size) {
|
||||
// Does not report Status
|
||||
pool_->Free(buffer, size);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// ParquetReadSource
|
||||
|
||||
ParquetReadSource::ParquetReadSource(ParquetAllocator* allocator)
|
||||
: file_(nullptr), allocator_(allocator) {}
|
||||
|
||||
Status ParquetReadSource::Open(const std::shared_ptr<io::ReadableFileInterface>& file) {
|
||||
int64_t file_size;
|
||||
RETURN_NOT_OK(file->GetSize(&file_size));
|
||||
|
||||
file_ = file;
|
||||
size_ = file_size;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void ParquetReadSource::Close() {
|
||||
// TODO(wesm): Make this a no-op for now. This leaves Python wrappers for
|
||||
// these classes in a borked state. Probably better to explicitly close.
|
||||
|
||||
// PARQUET_THROW_NOT_OK(file_->Close());
|
||||
}
|
||||
|
||||
int64_t ParquetReadSource::Tell() const {
|
||||
int64_t position;
|
||||
PARQUET_THROW_NOT_OK(file_->Tell(&position));
|
||||
return position;
|
||||
}
|
||||
|
||||
void ParquetReadSource::Seek(int64_t position) {
|
||||
PARQUET_THROW_NOT_OK(file_->Seek(position));
|
||||
}
|
||||
|
||||
int64_t ParquetReadSource::Read(int64_t nbytes, uint8_t* out) {
|
||||
int64_t bytes_read;
|
||||
PARQUET_THROW_NOT_OK(file_->Read(nbytes, &bytes_read, out));
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
std::shared_ptr<::parquet::Buffer> ParquetReadSource::Read(int64_t nbytes) {
|
||||
// TODO(wesm): This code is duplicated from parquet/util/input.cc; suggests
|
||||
// that there should be more code sharing amongst file-like sources
|
||||
auto result = std::make_shared<::parquet::OwnedMutableBuffer>(0, allocator_);
|
||||
result->Resize(nbytes);
|
||||
|
||||
int64_t bytes_read = Read(nbytes, result->mutable_data());
|
||||
if (bytes_read < nbytes) { result->Resize(bytes_read); }
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
@@ -1,84 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Bridges Arrow's IO interfaces and Parquet-cpp's IO interfaces
|
||||
|
||||
#ifndef ARROW_PARQUET_IO_H
|
||||
#define ARROW_PARQUET_IO_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "parquet/api/io.h"
|
||||
|
||||
#include "arrow/io/interfaces.h"
|
||||
#include "arrow/util/visibility.h"
|
||||
|
||||
namespace arrow {
|
||||
|
||||
class MemoryPool;
|
||||
|
||||
namespace parquet {
|
||||
|
||||
// An implementation of the Parquet MemoryAllocator API that plugs into an
|
||||
// existing Arrow memory pool. This way we can direct all allocations to a
|
||||
// single place rather than tracking allocations in different locations (for
|
||||
// example: without utilizing parquet-cpp's default allocator)
|
||||
class ARROW_EXPORT ParquetAllocator : public ::parquet::MemoryAllocator {
|
||||
public:
|
||||
// Uses the default memory pool
|
||||
ParquetAllocator();
|
||||
|
||||
explicit ParquetAllocator(MemoryPool* pool);
|
||||
virtual ~ParquetAllocator();
|
||||
|
||||
uint8_t* Malloc(int64_t size) override;
|
||||
void Free(uint8_t* buffer, int64_t size) override;
|
||||
|
||||
void set_pool(MemoryPool* pool) { pool_ = pool; }
|
||||
|
||||
MemoryPool* pool() const { return pool_; }
|
||||
|
||||
private:
|
||||
MemoryPool* pool_;
|
||||
};
|
||||
|
||||
class ARROW_EXPORT ParquetReadSource : public ::parquet::RandomAccessSource {
|
||||
public:
|
||||
explicit ParquetReadSource(ParquetAllocator* allocator);
|
||||
|
||||
// We need to ask for the file size on opening the file, and this can fail
|
||||
Status Open(const std::shared_ptr<io::ReadableFileInterface>& file);
|
||||
|
||||
void Close() override;
|
||||
int64_t Tell() const override;
|
||||
void Seek(int64_t pos) override;
|
||||
int64_t Read(int64_t nbytes, uint8_t* out) override;
|
||||
std::shared_ptr<::parquet::Buffer> Read(int64_t nbytes) override;
|
||||
|
||||
private:
|
||||
// An Arrow readable file of some kind
|
||||
std::shared_ptr<io::ReadableFileInterface> file_;
|
||||
|
||||
// The allocator is required for creating managed buffers
|
||||
ParquetAllocator* allocator_;
|
||||
};
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
|
||||
#endif // ARROW_PARQUET_IO_H
|
||||
@@ -1,135 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "arrow/io/memory.h"
|
||||
#include "arrow/parquet/io.h"
|
||||
#include "arrow/test-util.h"
|
||||
#include "arrow/util/memory-pool.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
#include "parquet/api/io.h"
|
||||
|
||||
namespace arrow {
|
||||
namespace parquet {
|
||||
|
||||
// Allocator tests
|
||||
|
||||
TEST(TestParquetAllocator, DefaultCtor) {
|
||||
ParquetAllocator allocator;
|
||||
|
||||
const int buffer_size = 10;
|
||||
|
||||
uint8_t* buffer = nullptr;
|
||||
ASSERT_NO_THROW(buffer = allocator.Malloc(buffer_size););
|
||||
|
||||
// valgrind will complain if we write into nullptr
|
||||
memset(buffer, 0, buffer_size);
|
||||
|
||||
allocator.Free(buffer, buffer_size);
|
||||
}
|
||||
|
||||
// Pass through to the default memory pool
|
||||
class TrackingPool : public MemoryPool {
|
||||
public:
|
||||
TrackingPool() : pool_(default_memory_pool()), bytes_allocated_(0) {}
|
||||
|
||||
Status Allocate(int64_t size, uint8_t** out) override {
|
||||
RETURN_NOT_OK(pool_->Allocate(size, out));
|
||||
bytes_allocated_ += size;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void Free(uint8_t* buffer, int64_t size) override {
|
||||
pool_->Free(buffer, size);
|
||||
bytes_allocated_ -= size;
|
||||
}
|
||||
|
||||
int64_t bytes_allocated() const override { return bytes_allocated_; }
|
||||
|
||||
private:
|
||||
MemoryPool* pool_;
|
||||
int64_t bytes_allocated_;
|
||||
};
|
||||
|
||||
TEST(TestParquetAllocator, CustomPool) {
|
||||
TrackingPool pool;
|
||||
|
||||
ParquetAllocator allocator(&pool);
|
||||
|
||||
ASSERT_EQ(&pool, allocator.pool());
|
||||
|
||||
const int buffer_size = 10;
|
||||
|
||||
uint8_t* buffer = nullptr;
|
||||
ASSERT_NO_THROW(buffer = allocator.Malloc(buffer_size););
|
||||
|
||||
ASSERT_EQ(buffer_size, pool.bytes_allocated());
|
||||
|
||||
// valgrind will complain if we write into nullptr
|
||||
memset(buffer, 0, buffer_size);
|
||||
|
||||
allocator.Free(buffer, buffer_size);
|
||||
|
||||
ASSERT_EQ(0, pool.bytes_allocated());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Read source tests
|
||||
|
||||
TEST(TestParquetReadSource, Basics) {
|
||||
std::string data = "this is the data";
|
||||
auto data_buffer = reinterpret_cast<const uint8_t*>(data.c_str());
|
||||
|
||||
ParquetAllocator allocator(default_memory_pool());
|
||||
|
||||
auto file = std::make_shared<io::BufferReader>(data_buffer, data.size());
|
||||
auto source = std::make_shared<ParquetReadSource>(&allocator);
|
||||
|
||||
ASSERT_OK(source->Open(file));
|
||||
|
||||
ASSERT_EQ(0, source->Tell());
|
||||
ASSERT_NO_THROW(source->Seek(5));
|
||||
ASSERT_EQ(5, source->Tell());
|
||||
ASSERT_NO_THROW(source->Seek(0));
|
||||
|
||||
// Seek out of bounds
|
||||
ASSERT_THROW(source->Seek(100), ::parquet::ParquetException);
|
||||
|
||||
uint8_t buffer[50];
|
||||
|
||||
ASSERT_NO_THROW(source->Read(4, buffer));
|
||||
ASSERT_EQ(0, std::memcmp(buffer, "this", 4));
|
||||
ASSERT_EQ(4, source->Tell());
|
||||
|
||||
std::shared_ptr<::parquet::Buffer> pq_buffer;
|
||||
|
||||
ASSERT_NO_THROW(pq_buffer = source->Read(7));
|
||||
|
||||
auto expected_buffer = std::make_shared<::parquet::Buffer>(data_buffer + 4, 7);
|
||||
|
||||
ASSERT_TRUE(expected_buffer->Equals(*pq_buffer.get()));
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
@@ -1,499 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "arrow/test-util.h"
|
||||
#include "arrow/parquet/test-util.h"
|
||||
#include "arrow/parquet/reader.h"
|
||||
#include "arrow/parquet/writer.h"
|
||||
#include "arrow/types/construct.h"
|
||||
#include "arrow/types/primitive.h"
|
||||
#include "arrow/types/string.h"
|
||||
#include "arrow/util/memory-pool.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
#include "parquet/api/reader.h"
|
||||
#include "parquet/api/writer.h"
|
||||
|
||||
using ParquetBuffer = parquet::Buffer;
|
||||
using parquet::BufferReader;
|
||||
using parquet::default_writer_properties;
|
||||
using parquet::InMemoryOutputStream;
|
||||
using parquet::LogicalType;
|
||||
using parquet::ParquetFileReader;
|
||||
using parquet::ParquetFileWriter;
|
||||
using parquet::RandomAccessSource;
|
||||
using parquet::Repetition;
|
||||
using parquet::SchemaDescriptor;
|
||||
using parquet::ParquetVersion;
|
||||
using ParquetType = parquet::Type;
|
||||
using parquet::schema::GroupNode;
|
||||
using parquet::schema::NodePtr;
|
||||
using parquet::schema::PrimitiveNode;
|
||||
|
||||
namespace arrow {
|
||||
|
||||
namespace parquet {
|
||||
|
||||
const int SMALL_SIZE = 100;
|
||||
const int LARGE_SIZE = 10000;
|
||||
|
||||
template <typename TestType>
|
||||
struct test_traits {};
|
||||
|
||||
template <>
|
||||
struct test_traits<BooleanType> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::BOOLEAN;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::NONE;
|
||||
static uint8_t const value;
|
||||
};
|
||||
|
||||
const uint8_t test_traits<BooleanType>::value(1);
|
||||
|
||||
template <>
|
||||
struct test_traits<UInt8Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::UINT_8;
|
||||
static uint8_t const value;
|
||||
};
|
||||
|
||||
const uint8_t test_traits<UInt8Type>::value(64);
|
||||
|
||||
template <>
|
||||
struct test_traits<Int8Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::INT_8;
|
||||
static int8_t const value;
|
||||
};
|
||||
|
||||
const int8_t test_traits<Int8Type>::value(-64);
|
||||
|
||||
template <>
|
||||
struct test_traits<UInt16Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::UINT_16;
|
||||
static uint16_t const value;
|
||||
};
|
||||
|
||||
const uint16_t test_traits<UInt16Type>::value(1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<Int16Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::INT_16;
|
||||
static int16_t const value;
|
||||
};
|
||||
|
||||
const int16_t test_traits<Int16Type>::value(-1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<UInt32Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::UINT_32;
|
||||
static uint32_t const value;
|
||||
};
|
||||
|
||||
const uint32_t test_traits<UInt32Type>::value(1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<Int32Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT32;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::NONE;
|
||||
static int32_t const value;
|
||||
};
|
||||
|
||||
const int32_t test_traits<Int32Type>::value(-1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<UInt64Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT64;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::UINT_64;
|
||||
static uint64_t const value;
|
||||
};
|
||||
|
||||
const uint64_t test_traits<UInt64Type>::value(1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<Int64Type> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT64;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::NONE;
|
||||
static int64_t const value;
|
||||
};
|
||||
|
||||
const int64_t test_traits<Int64Type>::value(-1024);
|
||||
|
||||
template <>
|
||||
struct test_traits<TimestampType> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::INT64;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::TIMESTAMP_MILLIS;
|
||||
static int64_t const value;
|
||||
};
|
||||
|
||||
const int64_t test_traits<TimestampType>::value(14695634030000);
|
||||
|
||||
template <>
|
||||
struct test_traits<FloatType> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::FLOAT;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::NONE;
|
||||
static float const value;
|
||||
};
|
||||
|
||||
const float test_traits<FloatType>::value(2.1f);
|
||||
|
||||
template <>
|
||||
struct test_traits<DoubleType> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::DOUBLE;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::NONE;
|
||||
static double const value;
|
||||
};
|
||||
|
||||
const double test_traits<DoubleType>::value(4.2);
|
||||
|
||||
template <>
|
||||
struct test_traits<StringType> {
|
||||
static constexpr ParquetType::type parquet_enum = ParquetType::BYTE_ARRAY;
|
||||
static constexpr LogicalType::type logical_enum = LogicalType::UTF8;
|
||||
static std::string const value;
|
||||
};
|
||||
|
||||
const std::string test_traits<StringType>::value("Test");
|
||||
|
||||
template <typename T>
|
||||
using ParquetDataType = ::parquet::DataType<test_traits<T>::parquet_enum>;
|
||||
|
||||
template <typename T>
|
||||
using ParquetWriter = ::parquet::TypedColumnWriter<ParquetDataType<T>>;
|
||||
|
||||
template <typename TestType>
|
||||
class TestParquetIO : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() {}
|
||||
|
||||
std::shared_ptr<GroupNode> MakeSchema(Repetition::type repetition) {
|
||||
auto pnode = PrimitiveNode::Make("column1", repetition,
|
||||
test_traits<TestType>::parquet_enum, test_traits<TestType>::logical_enum);
|
||||
NodePtr node_ =
|
||||
GroupNode::Make("schema", Repetition::REQUIRED, std::vector<NodePtr>({pnode}));
|
||||
return std::static_pointer_cast<GroupNode>(node_);
|
||||
}
|
||||
|
||||
std::unique_ptr<ParquetFileWriter> MakeWriter(
|
||||
const std::shared_ptr<GroupNode>& schema) {
|
||||
sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
return ParquetFileWriter::Open(sink_, schema);
|
||||
}
|
||||
|
||||
std::unique_ptr<ParquetFileReader> ReaderFromSink() {
|
||||
std::shared_ptr<ParquetBuffer> buffer = sink_->GetBuffer();
|
||||
std::unique_ptr<RandomAccessSource> source(new BufferReader(buffer));
|
||||
return ParquetFileReader::Open(std::move(source));
|
||||
}
|
||||
|
||||
void ReadSingleColumnFile(
|
||||
std::unique_ptr<ParquetFileReader> file_reader, std::shared_ptr<Array>* out) {
|
||||
arrow::parquet::FileReader reader(default_memory_pool(), std::move(file_reader));
|
||||
std::unique_ptr<arrow::parquet::FlatColumnReader> column_reader;
|
||||
ASSERT_OK_NO_THROW(reader.GetFlatColumn(0, &column_reader));
|
||||
ASSERT_NE(nullptr, column_reader.get());
|
||||
|
||||
ASSERT_OK(column_reader->NextBatch(SMALL_SIZE, out));
|
||||
ASSERT_NE(nullptr, out->get());
|
||||
}
|
||||
|
||||
void ReadAndCheckSingleColumnFile(Array* values) {
|
||||
std::shared_ptr<Array> out;
|
||||
ReadSingleColumnFile(ReaderFromSink(), &out);
|
||||
ASSERT_TRUE(values->Equals(out));
|
||||
}
|
||||
|
||||
void ReadTableFromFile(
|
||||
std::unique_ptr<ParquetFileReader> file_reader, std::shared_ptr<Table>* out) {
|
||||
arrow::parquet::FileReader reader(default_memory_pool(), std::move(file_reader));
|
||||
ASSERT_OK_NO_THROW(reader.ReadFlatTable(out));
|
||||
ASSERT_NE(nullptr, out->get());
|
||||
}
|
||||
|
||||
void ReadAndCheckSingleColumnTable(const std::shared_ptr<Array>& values) {
|
||||
std::shared_ptr<Table> out;
|
||||
ReadTableFromFile(ReaderFromSink(), &out);
|
||||
ASSERT_EQ(1, out->num_columns());
|
||||
ASSERT_EQ(values->length(), out->num_rows());
|
||||
|
||||
std::shared_ptr<ChunkedArray> chunked_array = out->column(0)->data();
|
||||
ASSERT_EQ(1, chunked_array->num_chunks());
|
||||
ASSERT_TRUE(values->Equals(chunked_array->chunk(0)));
|
||||
}
|
||||
|
||||
template <typename ArrayType>
|
||||
void WriteFlatColumn(const std::shared_ptr<GroupNode>& schema,
|
||||
const std::shared_ptr<ArrayType>& values) {
|
||||
FileWriter writer(default_memory_pool(), MakeWriter(schema));
|
||||
ASSERT_OK_NO_THROW(writer.NewRowGroup(values->length()));
|
||||
ASSERT_OK_NO_THROW(writer.WriteFlatColumnChunk(values.get()));
|
||||
ASSERT_OK_NO_THROW(writer.Close());
|
||||
}
|
||||
|
||||
std::shared_ptr<InMemoryOutputStream> sink_;
|
||||
};
|
||||
|
||||
// We habe separate tests for UInt32Type as this is currently the only type
|
||||
// where a roundtrip does not yield the identical Array structure.
|
||||
// There we write an UInt32 Array but receive an Int64 Array as result for
|
||||
// Parquet version 1.0.
|
||||
|
||||
typedef ::testing::Types<BooleanType, UInt8Type, Int8Type, UInt16Type, Int16Type,
|
||||
Int32Type, UInt64Type, Int64Type, TimestampType, FloatType, DoubleType,
|
||||
StringType> TestTypes;
|
||||
|
||||
TYPED_TEST_CASE(TestParquetIO, TestTypes);
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnRequiredWrite) {
|
||||
auto values = NonNullArray<TypeParam>(SMALL_SIZE);
|
||||
|
||||
std::shared_ptr<GroupNode> schema = this->MakeSchema(Repetition::REQUIRED);
|
||||
this->WriteFlatColumn(schema, values);
|
||||
|
||||
this->ReadAndCheckSingleColumnFile(values.get());
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnTableRequiredWrite) {
|
||||
auto values = NonNullArray<TypeParam>(SMALL_SIZE);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, false);
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
ASSERT_OK_NO_THROW(WriteFlatTable(table.get(), default_memory_pool(), this->sink_,
|
||||
values->length(), default_writer_properties()));
|
||||
|
||||
std::shared_ptr<Table> out;
|
||||
this->ReadTableFromFile(this->ReaderFromSink(), &out);
|
||||
ASSERT_EQ(1, out->num_columns());
|
||||
ASSERT_EQ(100, out->num_rows());
|
||||
|
||||
std::shared_ptr<ChunkedArray> chunked_array = out->column(0)->data();
|
||||
ASSERT_EQ(1, chunked_array->num_chunks());
|
||||
ASSERT_TRUE(values->Equals(chunked_array->chunk(0)));
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnOptionalReadWrite) {
|
||||
// This also tests max_definition_level = 1
|
||||
auto values = NullableArray<TypeParam>(SMALL_SIZE, 10);
|
||||
|
||||
std::shared_ptr<GroupNode> schema = this->MakeSchema(Repetition::OPTIONAL);
|
||||
this->WriteFlatColumn(schema, values);
|
||||
|
||||
this->ReadAndCheckSingleColumnFile(values.get());
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnTableOptionalReadWrite) {
|
||||
// This also tests max_definition_level = 1
|
||||
std::shared_ptr<Array> values = NullableArray<TypeParam>(SMALL_SIZE, 10);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, true);
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
ASSERT_OK_NO_THROW(WriteFlatTable(table.get(), default_memory_pool(), this->sink_,
|
||||
values->length(), default_writer_properties()));
|
||||
|
||||
this->ReadAndCheckSingleColumnTable(values);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnRequiredChunkedWrite) {
|
||||
auto values = NonNullArray<TypeParam>(SMALL_SIZE);
|
||||
int64_t chunk_size = values->length() / 4;
|
||||
|
||||
std::shared_ptr<GroupNode> schema = this->MakeSchema(Repetition::REQUIRED);
|
||||
FileWriter writer(default_memory_pool(), this->MakeWriter(schema));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_OK_NO_THROW(writer.NewRowGroup(chunk_size));
|
||||
ASSERT_OK_NO_THROW(
|
||||
writer.WriteFlatColumnChunk(values.get(), i * chunk_size, chunk_size));
|
||||
}
|
||||
ASSERT_OK_NO_THROW(writer.Close());
|
||||
|
||||
this->ReadAndCheckSingleColumnFile(values.get());
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnTableRequiredChunkedWrite) {
|
||||
auto values = NonNullArray<TypeParam>(LARGE_SIZE);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, false);
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
ASSERT_OK_NO_THROW(WriteFlatTable(
|
||||
table.get(), default_memory_pool(), this->sink_, 512, default_writer_properties()));
|
||||
|
||||
this->ReadAndCheckSingleColumnTable(values);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnOptionalChunkedWrite) {
|
||||
int64_t chunk_size = SMALL_SIZE / 4;
|
||||
auto values = NullableArray<TypeParam>(SMALL_SIZE, 10);
|
||||
|
||||
std::shared_ptr<GroupNode> schema = this->MakeSchema(Repetition::OPTIONAL);
|
||||
FileWriter writer(default_memory_pool(), this->MakeWriter(schema));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ASSERT_OK_NO_THROW(writer.NewRowGroup(chunk_size));
|
||||
ASSERT_OK_NO_THROW(
|
||||
writer.WriteFlatColumnChunk(values.get(), i * chunk_size, chunk_size));
|
||||
}
|
||||
ASSERT_OK_NO_THROW(writer.Close());
|
||||
|
||||
this->ReadAndCheckSingleColumnFile(values.get());
|
||||
}
|
||||
|
||||
TYPED_TEST(TestParquetIO, SingleColumnTableOptionalChunkedWrite) {
|
||||
// This also tests max_definition_level = 1
|
||||
auto values = NullableArray<TypeParam>(LARGE_SIZE, 100);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, true);
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
ASSERT_OK_NO_THROW(WriteFlatTable(
|
||||
table.get(), default_memory_pool(), this->sink_, 512, default_writer_properties()));
|
||||
|
||||
this->ReadAndCheckSingleColumnTable(values);
|
||||
}
|
||||
|
||||
using TestUInt32ParquetIO = TestParquetIO<UInt32Type>;
|
||||
|
||||
TEST_F(TestUInt32ParquetIO, Parquet_2_0_Compability) {
|
||||
// This also tests max_definition_level = 1
|
||||
std::shared_ptr<PrimitiveArray> values = NullableArray<UInt32Type>(LARGE_SIZE, 100);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, true);
|
||||
|
||||
// Parquet 2.0 roundtrip should yield an uint32_t column again
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
std::shared_ptr<::parquet::WriterProperties> properties =
|
||||
::parquet::WriterProperties::Builder()
|
||||
.version(ParquetVersion::PARQUET_2_0)
|
||||
->build();
|
||||
ASSERT_OK_NO_THROW(
|
||||
WriteFlatTable(table.get(), default_memory_pool(), this->sink_, 512, properties));
|
||||
this->ReadAndCheckSingleColumnTable(values);
|
||||
}
|
||||
|
||||
TEST_F(TestUInt32ParquetIO, Parquet_1_0_Compability) {
|
||||
// This also tests max_definition_level = 1
|
||||
std::shared_ptr<PrimitiveArray> values = NullableArray<UInt32Type>(LARGE_SIZE, 100);
|
||||
std::shared_ptr<Table> table = MakeSimpleTable(values, true);
|
||||
|
||||
// Parquet 1.0 returns an int64_t column as there is no way to tell a Parquet 1.0
|
||||
// reader that a column is unsigned.
|
||||
this->sink_ = std::make_shared<InMemoryOutputStream>();
|
||||
std::shared_ptr<::parquet::WriterProperties> properties =
|
||||
::parquet::WriterProperties::Builder()
|
||||
.version(ParquetVersion::PARQUET_1_0)
|
||||
->build();
|
||||
ASSERT_OK_NO_THROW(
|
||||
WriteFlatTable(table.get(), default_memory_pool(), this->sink_, 512, properties));
|
||||
|
||||
std::shared_ptr<Array> expected_values;
|
||||
std::shared_ptr<PoolBuffer> int64_data =
|
||||
std::make_shared<PoolBuffer>(default_memory_pool());
|
||||
{
|
||||
ASSERT_OK(int64_data->Resize(sizeof(int64_t) * values->length()));
|
||||
int64_t* int64_data_ptr = reinterpret_cast<int64_t*>(int64_data->mutable_data());
|
||||
const uint32_t* uint32_data_ptr =
|
||||
reinterpret_cast<const uint32_t*>(values->data()->data());
|
||||
// std::copy might be faster but this is explicit on the casts)
|
||||
for (int64_t i = 0; i < values->length(); i++) {
|
||||
int64_data_ptr[i] = static_cast<int64_t>(uint32_data_ptr[i]);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(MakePrimitiveArray(std::make_shared<Int64Type>(), values->length(),
|
||||
int64_data, values->null_count(), values->null_bitmap(), &expected_values));
|
||||
this->ReadAndCheckSingleColumnTable(expected_values);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using ParquetCDataType = typename ParquetDataType<T>::c_type;
|
||||
|
||||
template <typename TestType>
|
||||
class TestPrimitiveParquetIO : public TestParquetIO<TestType> {
|
||||
public:
|
||||
typedef typename TestType::c_type T;
|
||||
|
||||
void MakeTestFile(std::vector<T>& values, int num_chunks,
|
||||
std::unique_ptr<ParquetFileReader>* file_reader) {
|
||||
std::shared_ptr<GroupNode> schema = this->MakeSchema(Repetition::REQUIRED);
|
||||
std::unique_ptr<ParquetFileWriter> file_writer = this->MakeWriter(schema);
|
||||
size_t chunk_size = values.size() / num_chunks;
|
||||
// Convert to Parquet's expected physical type
|
||||
std::vector<uint8_t> values_buffer(
|
||||
sizeof(ParquetCDataType<TestType>) * values.size());
|
||||
auto values_parquet =
|
||||
reinterpret_cast<ParquetCDataType<TestType>*>(values_buffer.data());
|
||||
std::copy(values.cbegin(), values.cend(), values_parquet);
|
||||
for (int i = 0; i < num_chunks; i++) {
|
||||
auto row_group_writer = file_writer->AppendRowGroup(chunk_size);
|
||||
auto column_writer =
|
||||
static_cast<ParquetWriter<TestType>*>(row_group_writer->NextColumn());
|
||||
ParquetCDataType<TestType>* data = values_parquet + i * chunk_size;
|
||||
column_writer->WriteBatch(chunk_size, nullptr, nullptr, data);
|
||||
column_writer->Close();
|
||||
row_group_writer->Close();
|
||||
}
|
||||
file_writer->Close();
|
||||
*file_reader = this->ReaderFromSink();
|
||||
}
|
||||
|
||||
void CheckSingleColumnRequiredTableRead(int num_chunks) {
|
||||
std::vector<T> values(SMALL_SIZE, test_traits<TestType>::value);
|
||||
std::unique_ptr<ParquetFileReader> file_reader;
|
||||
ASSERT_NO_THROW(MakeTestFile(values, num_chunks, &file_reader));
|
||||
|
||||
std::shared_ptr<Table> out;
|
||||
this->ReadTableFromFile(std::move(file_reader), &out);
|
||||
ASSERT_EQ(1, out->num_columns());
|
||||
ASSERT_EQ(SMALL_SIZE, out->num_rows());
|
||||
|
||||
std::shared_ptr<ChunkedArray> chunked_array = out->column(0)->data();
|
||||
ASSERT_EQ(1, chunked_array->num_chunks());
|
||||
ExpectArray<TestType>(values.data(), chunked_array->chunk(0).get());
|
||||
}
|
||||
|
||||
void CheckSingleColumnRequiredRead(int num_chunks) {
|
||||
std::vector<T> values(SMALL_SIZE, test_traits<TestType>::value);
|
||||
std::unique_ptr<ParquetFileReader> file_reader;
|
||||
ASSERT_NO_THROW(MakeTestFile(values, num_chunks, &file_reader));
|
||||
|
||||
std::shared_ptr<Array> out;
|
||||
this->ReadSingleColumnFile(std::move(file_reader), &out);
|
||||
|
||||
ExpectArray<TestType>(values.data(), out.get());
|
||||
}
|
||||
};
|
||||
|
||||
typedef ::testing::Types<BooleanType, UInt8Type, Int8Type, UInt16Type, Int16Type,
|
||||
UInt32Type, Int32Type, UInt64Type, Int64Type, FloatType,
|
||||
DoubleType> PrimitiveTestTypes;
|
||||
|
||||
TYPED_TEST_CASE(TestPrimitiveParquetIO, PrimitiveTestTypes);
|
||||
|
||||
TYPED_TEST(TestPrimitiveParquetIO, SingleColumnRequiredRead) {
|
||||
this->CheckSingleColumnRequiredRead(1);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPrimitiveParquetIO, SingleColumnRequiredTableRead) {
|
||||
this->CheckSingleColumnRequiredTableRead(1);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPrimitiveParquetIO, SingleColumnRequiredChunkedRead) {
|
||||
this->CheckSingleColumnRequiredRead(4);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPrimitiveParquetIO, SingleColumnRequiredChunkedTableRead) {
|
||||
this->CheckSingleColumnRequiredTableRead(4);
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
@@ -1,261 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "arrow/test-util.h"
|
||||
#include "arrow/type.h"
|
||||
#include "arrow/types/datetime.h"
|
||||
#include "arrow/types/decimal.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
#include "arrow/parquet/schema.h"
|
||||
|
||||
using ParquetType = parquet::Type;
|
||||
using parquet::LogicalType;
|
||||
using parquet::Repetition;
|
||||
using parquet::schema::NodePtr;
|
||||
using parquet::schema::GroupNode;
|
||||
using parquet::schema::PrimitiveNode;
|
||||
|
||||
namespace arrow {
|
||||
|
||||
namespace parquet {
|
||||
|
||||
const auto BOOL = std::make_shared<BooleanType>();
|
||||
const auto UINT8 = std::make_shared<UInt8Type>();
|
||||
const auto INT32 = std::make_shared<Int32Type>();
|
||||
const auto INT64 = std::make_shared<Int64Type>();
|
||||
const auto FLOAT = std::make_shared<FloatType>();
|
||||
const auto DOUBLE = std::make_shared<DoubleType>();
|
||||
const auto UTF8 = std::make_shared<StringType>();
|
||||
const auto TIMESTAMP_MS = std::make_shared<TimestampType>(TimestampType::Unit::MILLI);
|
||||
// TODO: This requires parquet-cpp implementing the MICROS enum value
|
||||
// const auto TIMESTAMP_US = std::make_shared<TimestampType>(TimestampType::Unit::MICRO);
|
||||
const auto BINARY = std::make_shared<ListType>(std::make_shared<Field>("", UINT8));
|
||||
const auto DECIMAL_8_4 = std::make_shared<DecimalType>(8, 4);
|
||||
|
||||
class TestConvertParquetSchema : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() {}
|
||||
|
||||
void CheckFlatSchema(const std::shared_ptr<Schema>& expected_schema) {
|
||||
ASSERT_EQ(expected_schema->num_fields(), result_schema_->num_fields());
|
||||
for (int i = 0; i < expected_schema->num_fields(); ++i) {
|
||||
auto lhs = result_schema_->field(i);
|
||||
auto rhs = expected_schema->field(i);
|
||||
EXPECT_TRUE(lhs->Equals(rhs)) << i << " " << lhs->ToString()
|
||||
<< " != " << rhs->ToString();
|
||||
}
|
||||
}
|
||||
|
||||
Status ConvertSchema(const std::vector<NodePtr>& nodes) {
|
||||
NodePtr schema = GroupNode::Make("schema", Repetition::REPEATED, nodes);
|
||||
descr_.Init(schema);
|
||||
return FromParquetSchema(&descr_, &result_schema_);
|
||||
}
|
||||
|
||||
protected:
|
||||
::parquet::SchemaDescriptor descr_;
|
||||
std::shared_ptr<Schema> result_schema_;
|
||||
};
|
||||
|
||||
TEST_F(TestConvertParquetSchema, ParquetFlatPrimitives) {
|
||||
std::vector<NodePtr> parquet_fields;
|
||||
std::vector<std::shared_ptr<Field>> arrow_fields;
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("boolean", Repetition::REQUIRED, ParquetType::BOOLEAN));
|
||||
arrow_fields.push_back(std::make_shared<Field>("boolean", BOOL, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("int32", Repetition::REQUIRED, ParquetType::INT32));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int32", INT32, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("int64", Repetition::REQUIRED, ParquetType::INT64));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int64", INT64, false));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("timestamp", Repetition::REQUIRED,
|
||||
ParquetType::INT64, LogicalType::TIMESTAMP_MILLIS));
|
||||
arrow_fields.push_back(std::make_shared<Field>("timestamp", TIMESTAMP_MS, false));
|
||||
|
||||
// parquet_fields.push_back(PrimitiveNode::Make("timestamp", Repetition::REQUIRED,
|
||||
// ParquetType::INT64, LogicalType::TIMESTAMP_MICROS));
|
||||
// arrow_fields.push_back(std::make_shared<Field>("timestamp", TIMESTAMP_US, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("float", Repetition::OPTIONAL, ParquetType::FLOAT));
|
||||
arrow_fields.push_back(std::make_shared<Field>("float", FLOAT));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("double", Repetition::OPTIONAL, ParquetType::DOUBLE));
|
||||
arrow_fields.push_back(std::make_shared<Field>("double", DOUBLE));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("binary", Repetition::OPTIONAL, ParquetType::BYTE_ARRAY));
|
||||
arrow_fields.push_back(std::make_shared<Field>("binary", BINARY));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make(
|
||||
"string", Repetition::OPTIONAL, ParquetType::BYTE_ARRAY, LogicalType::UTF8));
|
||||
arrow_fields.push_back(std::make_shared<Field>("string", UTF8));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("flba-binary", Repetition::OPTIONAL,
|
||||
ParquetType::FIXED_LEN_BYTE_ARRAY, LogicalType::NONE, 12));
|
||||
arrow_fields.push_back(std::make_shared<Field>("flba-binary", BINARY));
|
||||
|
||||
auto arrow_schema = std::make_shared<Schema>(arrow_fields);
|
||||
ASSERT_OK(ConvertSchema(parquet_fields));
|
||||
|
||||
CheckFlatSchema(arrow_schema);
|
||||
}
|
||||
|
||||
TEST_F(TestConvertParquetSchema, ParquetFlatDecimals) {
|
||||
std::vector<NodePtr> parquet_fields;
|
||||
std::vector<std::shared_ptr<Field>> arrow_fields;
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("flba-decimal", Repetition::OPTIONAL,
|
||||
ParquetType::FIXED_LEN_BYTE_ARRAY, LogicalType::DECIMAL, 4, 8, 4));
|
||||
arrow_fields.push_back(std::make_shared<Field>("flba-decimal", DECIMAL_8_4));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("binary-decimal", Repetition::OPTIONAL,
|
||||
ParquetType::BYTE_ARRAY, LogicalType::DECIMAL, -1, 8, 4));
|
||||
arrow_fields.push_back(std::make_shared<Field>("binary-decimal", DECIMAL_8_4));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("int32-decimal", Repetition::OPTIONAL,
|
||||
ParquetType::INT32, LogicalType::DECIMAL, -1, 8, 4));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int32-decimal", DECIMAL_8_4));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("int64-decimal", Repetition::OPTIONAL,
|
||||
ParquetType::INT64, LogicalType::DECIMAL, -1, 8, 4));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int64-decimal", DECIMAL_8_4));
|
||||
|
||||
auto arrow_schema = std::make_shared<Schema>(arrow_fields);
|
||||
ASSERT_OK(ConvertSchema(parquet_fields));
|
||||
|
||||
CheckFlatSchema(arrow_schema);
|
||||
}
|
||||
|
||||
TEST_F(TestConvertParquetSchema, UnsupportedThings) {
|
||||
std::vector<NodePtr> unsupported_nodes;
|
||||
|
||||
unsupported_nodes.push_back(
|
||||
PrimitiveNode::Make("int96", Repetition::REQUIRED, ParquetType::INT96));
|
||||
|
||||
unsupported_nodes.push_back(
|
||||
GroupNode::Make("repeated-group", Repetition::REPEATED, {}));
|
||||
|
||||
unsupported_nodes.push_back(PrimitiveNode::Make(
|
||||
"int32", Repetition::OPTIONAL, ParquetType::INT32, LogicalType::DATE));
|
||||
|
||||
for (const NodePtr& node : unsupported_nodes) {
|
||||
ASSERT_RAISES(NotImplemented, ConvertSchema({node}));
|
||||
}
|
||||
}
|
||||
|
||||
class TestConvertArrowSchema : public ::testing::Test {
|
||||
public:
|
||||
virtual void SetUp() {}
|
||||
|
||||
void CheckFlatSchema(const std::vector<NodePtr>& nodes) {
|
||||
NodePtr schema_node = GroupNode::Make("schema", Repetition::REPEATED, nodes);
|
||||
const GroupNode* expected_schema_node =
|
||||
static_cast<const GroupNode*>(schema_node.get());
|
||||
const GroupNode* result_schema_node = result_schema_->group_node();
|
||||
|
||||
ASSERT_EQ(expected_schema_node->field_count(), result_schema_node->field_count());
|
||||
|
||||
for (int i = 0; i < expected_schema_node->field_count(); i++) {
|
||||
auto lhs = result_schema_node->field(i);
|
||||
auto rhs = expected_schema_node->field(i);
|
||||
EXPECT_TRUE(lhs->Equals(rhs.get()));
|
||||
}
|
||||
}
|
||||
|
||||
Status ConvertSchema(const std::vector<std::shared_ptr<Field>>& fields) {
|
||||
arrow_schema_ = std::make_shared<Schema>(fields);
|
||||
std::shared_ptr<::parquet::WriterProperties> properties =
|
||||
::parquet::default_writer_properties();
|
||||
return ToParquetSchema(arrow_schema_.get(), *properties.get(), &result_schema_);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Schema> arrow_schema_;
|
||||
std::shared_ptr<::parquet::SchemaDescriptor> result_schema_;
|
||||
};
|
||||
|
||||
TEST_F(TestConvertArrowSchema, ParquetFlatPrimitives) {
|
||||
std::vector<NodePtr> parquet_fields;
|
||||
std::vector<std::shared_ptr<Field>> arrow_fields;
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("boolean", Repetition::REQUIRED, ParquetType::BOOLEAN));
|
||||
arrow_fields.push_back(std::make_shared<Field>("boolean", BOOL, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("int32", Repetition::REQUIRED, ParquetType::INT32));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int32", INT32, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("int64", Repetition::REQUIRED, ParquetType::INT64));
|
||||
arrow_fields.push_back(std::make_shared<Field>("int64", INT64, false));
|
||||
|
||||
parquet_fields.push_back(PrimitiveNode::Make("timestamp", Repetition::REQUIRED,
|
||||
ParquetType::INT64, LogicalType::TIMESTAMP_MILLIS));
|
||||
arrow_fields.push_back(std::make_shared<Field>("timestamp", TIMESTAMP_MS, false));
|
||||
|
||||
// parquet_fields.push_back(PrimitiveNode::Make("timestamp", Repetition::REQUIRED,
|
||||
// ParquetType::INT64, LogicalType::TIMESTAMP_MICROS));
|
||||
// arrow_fields.push_back(std::make_shared<Field>("timestamp", TIMESTAMP_US, false));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("float", Repetition::OPTIONAL, ParquetType::FLOAT));
|
||||
arrow_fields.push_back(std::make_shared<Field>("float", FLOAT));
|
||||
|
||||
parquet_fields.push_back(
|
||||
PrimitiveNode::Make("double", Repetition::OPTIONAL, ParquetType::DOUBLE));
|
||||
arrow_fields.push_back(std::make_shared<Field>("double", DOUBLE));
|
||||
|
||||
// TODO: String types need to be clarified a bit more in the Arrow spec
|
||||
parquet_fields.push_back(PrimitiveNode::Make(
|
||||
"string", Repetition::OPTIONAL, ParquetType::BYTE_ARRAY, LogicalType::UTF8));
|
||||
arrow_fields.push_back(std::make_shared<Field>("string", UTF8));
|
||||
|
||||
ASSERT_OK(ConvertSchema(arrow_fields));
|
||||
|
||||
CheckFlatSchema(parquet_fields);
|
||||
}
|
||||
|
||||
TEST_F(TestConvertArrowSchema, ParquetFlatDecimals) {
|
||||
std::vector<NodePtr> parquet_fields;
|
||||
std::vector<std::shared_ptr<Field>> arrow_fields;
|
||||
|
||||
// TODO: Test Decimal Arrow -> Parquet conversion
|
||||
|
||||
ASSERT_OK(ConvertSchema(arrow_fields));
|
||||
|
||||
CheckFlatSchema(parquet_fields);
|
||||
}
|
||||
|
||||
TEST(TestNodeConversion, DateAndTime) {}
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
@@ -1,401 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include "arrow/parquet/reader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "arrow/column.h"
|
||||
#include "arrow/parquet/io.h"
|
||||
#include "arrow/parquet/schema.h"
|
||||
#include "arrow/parquet/utils.h"
|
||||
#include "arrow/schema.h"
|
||||
#include "arrow/table.h"
|
||||
#include "arrow/types/primitive.h"
|
||||
#include "arrow/types/string.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
using parquet::ColumnReader;
|
||||
using parquet::Repetition;
|
||||
using parquet::TypedColumnReader;
|
||||
|
||||
// Help reduce verbosity
|
||||
using ParquetRAS = parquet::RandomAccessSource;
|
||||
using ParquetReader = parquet::ParquetFileReader;
|
||||
|
||||
namespace arrow {
|
||||
namespace parquet {
|
||||
|
||||
template <typename ArrowType>
|
||||
struct ArrowTypeTraits {
|
||||
typedef NumericBuilder<ArrowType> builder_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ArrowTypeTraits<BooleanType> {
|
||||
typedef BooleanBuilder builder_type;
|
||||
};
|
||||
|
||||
template <typename ArrowType>
|
||||
using BuilderType = typename ArrowTypeTraits<ArrowType>::builder_type;
|
||||
|
||||
class FileReader::Impl {
|
||||
public:
|
||||
Impl(MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileReader> reader);
|
||||
virtual ~Impl() {}
|
||||
|
||||
bool CheckForFlatColumn(const ::parquet::ColumnDescriptor* descr);
|
||||
Status GetFlatColumn(int i, std::unique_ptr<FlatColumnReader>* out);
|
||||
Status ReadFlatColumn(int i, std::shared_ptr<Array>* out);
|
||||
Status ReadFlatTable(std::shared_ptr<Table>* out);
|
||||
|
||||
private:
|
||||
MemoryPool* pool_;
|
||||
std::unique_ptr<::parquet::ParquetFileReader> reader_;
|
||||
};
|
||||
|
||||
class FlatColumnReader::Impl {
|
||||
public:
|
||||
Impl(MemoryPool* pool, const ::parquet::ColumnDescriptor* descr,
|
||||
::parquet::ParquetFileReader* reader, int column_index);
|
||||
virtual ~Impl() {}
|
||||
|
||||
Status NextBatch(int batch_size, std::shared_ptr<Array>* out);
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status TypedReadBatch(int batch_size, std::shared_ptr<Array>* out);
|
||||
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status ReadNullableFlatBatch(const int16_t* def_levels,
|
||||
typename ParquetType::c_type* values, int64_t values_read, int64_t levels_read,
|
||||
BuilderType<ArrowType>* builder);
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status ReadNonNullableBatch(typename ParquetType::c_type* values, int64_t values_read,
|
||||
BuilderType<ArrowType>* builder);
|
||||
|
||||
private:
|
||||
void NextRowGroup();
|
||||
|
||||
template <typename InType, typename OutType>
|
||||
struct can_copy_ptr {
|
||||
static constexpr bool value =
|
||||
std::is_same<InType, OutType>::value ||
|
||||
(std::is_integral<InType>{} && std::is_integral<OutType>{} &&
|
||||
(sizeof(InType) == sizeof(OutType)));
|
||||
};
|
||||
|
||||
template <typename InType, typename OutType,
|
||||
typename std::enable_if<can_copy_ptr<InType, OutType>::value>::type* = nullptr>
|
||||
Status ConvertPhysicalType(
|
||||
const InType* in_ptr, int64_t length, const OutType** out_ptr) {
|
||||
*out_ptr = reinterpret_cast<const OutType*>(in_ptr);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename InType, typename OutType,
|
||||
typename std::enable_if<not can_copy_ptr<InType, OutType>::value>::type* = nullptr>
|
||||
Status ConvertPhysicalType(
|
||||
const InType* in_ptr, int64_t length, const OutType** out_ptr) {
|
||||
RETURN_NOT_OK(values_builder_buffer_.Resize(length * sizeof(OutType)));
|
||||
OutType* mutable_out_ptr =
|
||||
reinterpret_cast<OutType*>(values_builder_buffer_.mutable_data());
|
||||
std::copy(in_ptr, in_ptr + length, mutable_out_ptr);
|
||||
*out_ptr = mutable_out_ptr;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
MemoryPool* pool_;
|
||||
const ::parquet::ColumnDescriptor* descr_;
|
||||
::parquet::ParquetFileReader* reader_;
|
||||
int column_index_;
|
||||
int next_row_group_;
|
||||
std::shared_ptr<ColumnReader> column_reader_;
|
||||
std::shared_ptr<Field> field_;
|
||||
|
||||
PoolBuffer values_buffer_;
|
||||
PoolBuffer def_levels_buffer_;
|
||||
PoolBuffer values_builder_buffer_;
|
||||
PoolBuffer valid_bytes_buffer_;
|
||||
};
|
||||
|
||||
FileReader::Impl::Impl(
|
||||
MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileReader> reader)
|
||||
: pool_(pool), reader_(std::move(reader)) {}
|
||||
|
||||
bool FileReader::Impl::CheckForFlatColumn(const ::parquet::ColumnDescriptor* descr) {
|
||||
if ((descr->max_repetition_level() > 0) || (descr->max_definition_level() > 1)) {
|
||||
return false;
|
||||
} else if ((descr->max_definition_level() == 1) &&
|
||||
(descr->schema_node()->repetition() != Repetition::OPTIONAL)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Status FileReader::Impl::GetFlatColumn(int i, std::unique_ptr<FlatColumnReader>* out) {
|
||||
const ::parquet::SchemaDescriptor* schema = reader_->metadata()->schema();
|
||||
|
||||
if (!CheckForFlatColumn(schema->Column(i))) {
|
||||
return Status::Invalid("The requested column is not flat");
|
||||
}
|
||||
std::unique_ptr<FlatColumnReader::Impl> impl(
|
||||
new FlatColumnReader::Impl(pool_, schema->Column(i), reader_.get(), i));
|
||||
*out = std::unique_ptr<FlatColumnReader>(new FlatColumnReader(std::move(impl)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FileReader::Impl::ReadFlatColumn(int i, std::shared_ptr<Array>* out) {
|
||||
std::unique_ptr<FlatColumnReader> flat_column_reader;
|
||||
RETURN_NOT_OK(GetFlatColumn(i, &flat_column_reader));
|
||||
return flat_column_reader->NextBatch(reader_->metadata()->num_rows(), out);
|
||||
}
|
||||
|
||||
Status FileReader::Impl::ReadFlatTable(std::shared_ptr<Table>* table) {
|
||||
auto descr = reader_->metadata()->schema();
|
||||
|
||||
const std::string& name = descr->name();
|
||||
std::shared_ptr<Schema> schema;
|
||||
RETURN_NOT_OK(FromParquetSchema(descr, &schema));
|
||||
|
||||
int num_columns = reader_->metadata()->num_columns();
|
||||
|
||||
std::vector<std::shared_ptr<Column>> columns(num_columns);
|
||||
for (int i = 0; i < num_columns; i++) {
|
||||
std::shared_ptr<Array> array;
|
||||
RETURN_NOT_OK(ReadFlatColumn(i, &array));
|
||||
columns[i] = std::make_shared<Column>(schema->field(i), array);
|
||||
}
|
||||
|
||||
*table = std::make_shared<Table>(name, schema, columns);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
FileReader::FileReader(
|
||||
MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileReader> reader)
|
||||
: impl_(new FileReader::Impl(pool, std::move(reader))) {}
|
||||
|
||||
FileReader::~FileReader() {}
|
||||
|
||||
// Static ctor
|
||||
Status OpenFile(const std::shared_ptr<io::ReadableFileInterface>& file,
|
||||
ParquetAllocator* allocator, std::unique_ptr<FileReader>* reader) {
|
||||
std::unique_ptr<ParquetReadSource> source(new ParquetReadSource(allocator));
|
||||
RETURN_NOT_OK(source->Open(file));
|
||||
|
||||
// TODO(wesm): reader properties
|
||||
std::unique_ptr<ParquetReader> pq_reader;
|
||||
PARQUET_CATCH_NOT_OK(pq_reader = ParquetReader::Open(std::move(source)));
|
||||
|
||||
// Use the same memory pool as the ParquetAllocator
|
||||
reader->reset(new FileReader(allocator->pool(), std::move(pq_reader)));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FileReader::GetFlatColumn(int i, std::unique_ptr<FlatColumnReader>* out) {
|
||||
return impl_->GetFlatColumn(i, out);
|
||||
}
|
||||
|
||||
Status FileReader::ReadFlatColumn(int i, std::shared_ptr<Array>* out) {
|
||||
return impl_->ReadFlatColumn(i, out);
|
||||
}
|
||||
|
||||
Status FileReader::ReadFlatTable(std::shared_ptr<Table>* out) {
|
||||
return impl_->ReadFlatTable(out);
|
||||
}
|
||||
|
||||
FlatColumnReader::Impl::Impl(MemoryPool* pool, const ::parquet::ColumnDescriptor* descr,
|
||||
::parquet::ParquetFileReader* reader, int column_index)
|
||||
: pool_(pool),
|
||||
descr_(descr),
|
||||
reader_(reader),
|
||||
column_index_(column_index),
|
||||
next_row_group_(0),
|
||||
values_buffer_(pool),
|
||||
def_levels_buffer_(pool) {
|
||||
NodeToField(descr_->schema_node(), &field_);
|
||||
NextRowGroup();
|
||||
}
|
||||
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status FlatColumnReader::Impl::ReadNonNullableBatch(typename ParquetType::c_type* values,
|
||||
int64_t values_read, BuilderType<ArrowType>* builder) {
|
||||
using ArrowCType = typename ArrowType::c_type;
|
||||
using ParquetCType = typename ParquetType::c_type;
|
||||
|
||||
DCHECK(builder);
|
||||
const ArrowCType* values_ptr = nullptr;
|
||||
RETURN_NOT_OK(
|
||||
(ConvertPhysicalType<ParquetCType, ArrowCType>(values, values_read, &values_ptr)));
|
||||
RETURN_NOT_OK(builder->Append(values_ptr, values_read));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status FlatColumnReader::Impl::ReadNullableFlatBatch(const int16_t* def_levels,
|
||||
typename ParquetType::c_type* values, int64_t values_read, int64_t levels_read,
|
||||
BuilderType<ArrowType>* builder) {
|
||||
using ArrowCType = typename ArrowType::c_type;
|
||||
|
||||
DCHECK(builder);
|
||||
RETURN_NOT_OK(values_builder_buffer_.Resize(levels_read * sizeof(ArrowCType)));
|
||||
RETURN_NOT_OK(valid_bytes_buffer_.Resize(levels_read * sizeof(uint8_t)));
|
||||
auto values_ptr = reinterpret_cast<ArrowCType*>(values_builder_buffer_.mutable_data());
|
||||
uint8_t* valid_bytes = valid_bytes_buffer_.mutable_data();
|
||||
int values_idx = 0;
|
||||
for (int64_t i = 0; i < levels_read; i++) {
|
||||
if (def_levels[i] < descr_->max_definition_level()) {
|
||||
valid_bytes[i] = 0;
|
||||
} else {
|
||||
valid_bytes[i] = 1;
|
||||
values_ptr[i] = values[values_idx++];
|
||||
}
|
||||
}
|
||||
RETURN_NOT_OK(builder->Append(values_ptr, levels_read, valid_bytes));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename ArrowType, typename ParquetType>
|
||||
Status FlatColumnReader::Impl::TypedReadBatch(
|
||||
int batch_size, std::shared_ptr<Array>* out) {
|
||||
using ParquetCType = typename ParquetType::c_type;
|
||||
|
||||
int values_to_read = batch_size;
|
||||
BuilderType<ArrowType> builder(pool_, field_->type);
|
||||
while ((values_to_read > 0) && column_reader_) {
|
||||
values_buffer_.Resize(values_to_read * sizeof(ParquetCType));
|
||||
if (descr_->max_definition_level() > 0) {
|
||||
def_levels_buffer_.Resize(values_to_read * sizeof(int16_t));
|
||||
}
|
||||
auto reader = dynamic_cast<TypedColumnReader<ParquetType>*>(column_reader_.get());
|
||||
int64_t values_read;
|
||||
int64_t levels_read;
|
||||
int16_t* def_levels = reinterpret_cast<int16_t*>(def_levels_buffer_.mutable_data());
|
||||
auto values = reinterpret_cast<ParquetCType*>(values_buffer_.mutable_data());
|
||||
PARQUET_CATCH_NOT_OK(levels_read = reader->ReadBatch(
|
||||
values_to_read, def_levels, nullptr, values, &values_read));
|
||||
values_to_read -= levels_read;
|
||||
if (descr_->max_definition_level() == 0) {
|
||||
RETURN_NOT_OK(
|
||||
(ReadNonNullableBatch<ArrowType, ParquetType>(values, values_read, &builder)));
|
||||
} else {
|
||||
// As per the defintion and checks for flat columns:
|
||||
// descr_->max_definition_level() == 1
|
||||
RETURN_NOT_OK((ReadNullableFlatBatch<ArrowType, ParquetType>(
|
||||
def_levels, values, values_read, levels_read, &builder)));
|
||||
}
|
||||
if (!column_reader_->HasNext()) { NextRowGroup(); }
|
||||
}
|
||||
*out = builder.Finish();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <>
|
||||
Status FlatColumnReader::Impl::TypedReadBatch<StringType, ::parquet::ByteArrayType>(
|
||||
int batch_size, std::shared_ptr<Array>* out) {
|
||||
int values_to_read = batch_size;
|
||||
StringBuilder builder(pool_, field_->type);
|
||||
while ((values_to_read > 0) && column_reader_) {
|
||||
values_buffer_.Resize(values_to_read * sizeof(::parquet::ByteArray));
|
||||
if (descr_->max_definition_level() > 0) {
|
||||
def_levels_buffer_.Resize(values_to_read * sizeof(int16_t));
|
||||
}
|
||||
auto reader =
|
||||
dynamic_cast<TypedColumnReader<::parquet::ByteArrayType>*>(column_reader_.get());
|
||||
int64_t values_read;
|
||||
int64_t levels_read;
|
||||
int16_t* def_levels = reinterpret_cast<int16_t*>(def_levels_buffer_.mutable_data());
|
||||
auto values = reinterpret_cast<::parquet::ByteArray*>(values_buffer_.mutable_data());
|
||||
PARQUET_CATCH_NOT_OK(levels_read = reader->ReadBatch(
|
||||
values_to_read, def_levels, nullptr, values, &values_read));
|
||||
values_to_read -= levels_read;
|
||||
if (descr_->max_definition_level() == 0) {
|
||||
for (int64_t i = 0; i < levels_read; i++) {
|
||||
RETURN_NOT_OK(
|
||||
builder.Append(reinterpret_cast<const char*>(values[i].ptr), values[i].len));
|
||||
}
|
||||
} else {
|
||||
// descr_->max_definition_level() == 1
|
||||
int values_idx = 0;
|
||||
for (int64_t i = 0; i < levels_read; i++) {
|
||||
if (def_levels[i] < descr_->max_definition_level()) {
|
||||
RETURN_NOT_OK(builder.AppendNull());
|
||||
} else {
|
||||
RETURN_NOT_OK(
|
||||
builder.Append(reinterpret_cast<const char*>(values[values_idx].ptr),
|
||||
values[values_idx].len));
|
||||
values_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!column_reader_->HasNext()) { NextRowGroup(); }
|
||||
}
|
||||
*out = builder.Finish();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#define TYPED_BATCH_CASE(ENUM, ArrowType, ParquetType) \
|
||||
case Type::ENUM: \
|
||||
return TypedReadBatch<ArrowType, ParquetType>(batch_size, out); \
|
||||
break;
|
||||
|
||||
Status FlatColumnReader::Impl::NextBatch(int batch_size, std::shared_ptr<Array>* out) {
|
||||
if (!column_reader_) {
|
||||
// Exhausted all row groups.
|
||||
*out = nullptr;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
switch (field_->type->type) {
|
||||
TYPED_BATCH_CASE(BOOL, BooleanType, ::parquet::BooleanType)
|
||||
TYPED_BATCH_CASE(UINT8, UInt8Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(INT8, Int8Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(UINT16, UInt16Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(INT16, Int16Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(UINT32, UInt32Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(INT32, Int32Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(UINT64, UInt64Type, ::parquet::Int64Type)
|
||||
TYPED_BATCH_CASE(INT64, Int64Type, ::parquet::Int64Type)
|
||||
TYPED_BATCH_CASE(FLOAT, FloatType, ::parquet::FloatType)
|
||||
TYPED_BATCH_CASE(DOUBLE, DoubleType, ::parquet::DoubleType)
|
||||
TYPED_BATCH_CASE(STRING, StringType, ::parquet::ByteArrayType)
|
||||
TYPED_BATCH_CASE(TIMESTAMP, TimestampType, ::parquet::Int64Type)
|
||||
default:
|
||||
return Status::NotImplemented(field_->type->ToString());
|
||||
}
|
||||
}
|
||||
|
||||
void FlatColumnReader::Impl::NextRowGroup() {
|
||||
if (next_row_group_ < reader_->metadata()->num_row_groups()) {
|
||||
column_reader_ = reader_->RowGroup(next_row_group_)->Column(column_index_);
|
||||
next_row_group_++;
|
||||
} else {
|
||||
column_reader_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FlatColumnReader::FlatColumnReader(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {}
|
||||
|
||||
FlatColumnReader::~FlatColumnReader() {}
|
||||
|
||||
Status FlatColumnReader::NextBatch(int batch_size, std::shared_ptr<Array>* out) {
|
||||
return impl_->NextBatch(batch_size, out);
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
@@ -1,146 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#ifndef ARROW_PARQUET_READER_H
|
||||
#define ARROW_PARQUET_READER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "parquet/api/reader.h"
|
||||
#include "parquet/api/schema.h"
|
||||
|
||||
#include "arrow/io/interfaces.h"
|
||||
#include "arrow/parquet/io.h"
|
||||
#include "arrow/util/visibility.h"
|
||||
|
||||
namespace arrow {
|
||||
|
||||
class Array;
|
||||
class MemoryPool;
|
||||
class RecordBatch;
|
||||
class Status;
|
||||
class Table;
|
||||
|
||||
namespace parquet {
|
||||
|
||||
class FlatColumnReader;
|
||||
|
||||
// Arrow read adapter class for deserializing Parquet files as Arrow row
|
||||
// batches.
|
||||
//
|
||||
// TODO(wesm): nested data does not always make sense with this user
|
||||
// interface unless you are only reading a single leaf node from a branch of
|
||||
// a table. For example:
|
||||
//
|
||||
// repeated group data {
|
||||
// optional group record {
|
||||
// optional int32 val1;
|
||||
// optional byte_array val2;
|
||||
// optional bool val3;
|
||||
// }
|
||||
// optional int32 val4;
|
||||
// }
|
||||
//
|
||||
// In the Parquet file, there are 3 leaf nodes:
|
||||
//
|
||||
// * data.record.val1
|
||||
// * data.record.val2
|
||||
// * data.record.val3
|
||||
// * data.val4
|
||||
//
|
||||
// When materializing this data in an Arrow array, we would have:
|
||||
//
|
||||
// data: list<struct<
|
||||
// record: struct<
|
||||
// val1: int32,
|
||||
// val2: string (= list<uint8>),
|
||||
// val3: bool,
|
||||
// >,
|
||||
// val4: int32
|
||||
// >>
|
||||
//
|
||||
// However, in the Parquet format, each leaf node has its own repetition and
|
||||
// definition levels describing the structure of the intermediate nodes in
|
||||
// this array structure. Thus, we will need to scan the leaf data for a group
|
||||
// of leaf nodes part of the same type tree to create a single result Arrow
|
||||
// nested array structure.
|
||||
//
|
||||
// This is additionally complicated "chunky" repeated fields or very large byte
|
||||
// arrays
|
||||
class ARROW_EXPORT FileReader {
|
||||
public:
|
||||
FileReader(MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileReader> reader);
|
||||
|
||||
// Since the distribution of columns amongst a Parquet file's row groups may
|
||||
// be uneven (the number of values in each column chunk can be different), we
|
||||
// provide a column-oriented read interface. The ColumnReader hides the
|
||||
// details of paging through the file's row groups and yielding
|
||||
// fully-materialized arrow::Array instances
|
||||
//
|
||||
// Returns error status if the column of interest is not flat.
|
||||
Status GetFlatColumn(int i, std::unique_ptr<FlatColumnReader>* out);
|
||||
// Read column as a whole into an Array.
|
||||
Status ReadFlatColumn(int i, std::shared_ptr<Array>* out);
|
||||
// Read a table of flat columns into a Table.
|
||||
Status ReadFlatTable(std::shared_ptr<Table>* out);
|
||||
|
||||
virtual ~FileReader();
|
||||
|
||||
private:
|
||||
class ARROW_NO_EXPORT Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
// At this point, the column reader is a stream iterator. It only knows how to
|
||||
// read the next batch of values for a particular column from the file until it
|
||||
// runs out.
|
||||
//
|
||||
// We also do not expose any internal Parquet details, such as row groups. This
|
||||
// might change in the future.
|
||||
class ARROW_EXPORT FlatColumnReader {
|
||||
public:
|
||||
virtual ~FlatColumnReader();
|
||||
|
||||
// Scan the next array of the indicated size. The actual size of the
|
||||
// returned array may be less than the passed size depending how much data is
|
||||
// available in the file.
|
||||
//
|
||||
// When all the data in the file has been exhausted, the result is set to
|
||||
// nullptr.
|
||||
//
|
||||
// Returns Status::OK on a successful read, including if you have exhausted
|
||||
// the data available in the file.
|
||||
Status NextBatch(int batch_size, std::shared_ptr<Array>* out);
|
||||
|
||||
private:
|
||||
class ARROW_NO_EXPORT Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
explicit FlatColumnReader(std::unique_ptr<Impl> impl);
|
||||
|
||||
friend class FileReader;
|
||||
};
|
||||
|
||||
// Helper function to create a file reader from an implementation of an Arrow
|
||||
// readable file
|
||||
ARROW_EXPORT
|
||||
Status OpenFile(const std::shared_ptr<io::ReadableFileInterface>& file,
|
||||
ParquetAllocator* allocator, std::unique_ptr<FileReader>* reader);
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
|
||||
#endif // ARROW_PARQUET_READER_H
|
||||
@@ -1,344 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include "arrow/parquet/schema.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "parquet/api/schema.h"
|
||||
|
||||
#include "arrow/parquet/utils.h"
|
||||
#include "arrow/types/decimal.h"
|
||||
#include "arrow/types/string.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
using parquet::Repetition;
|
||||
using parquet::schema::Node;
|
||||
using parquet::schema::NodePtr;
|
||||
using parquet::schema::GroupNode;
|
||||
using parquet::schema::PrimitiveNode;
|
||||
|
||||
using ParquetType = parquet::Type;
|
||||
using parquet::LogicalType;
|
||||
|
||||
namespace arrow {
|
||||
|
||||
namespace parquet {
|
||||
|
||||
const auto BOOL = std::make_shared<BooleanType>();
|
||||
const auto UINT8 = std::make_shared<UInt8Type>();
|
||||
const auto INT8 = std::make_shared<Int8Type>();
|
||||
const auto UINT16 = std::make_shared<UInt16Type>();
|
||||
const auto INT16 = std::make_shared<Int16Type>();
|
||||
const auto UINT32 = std::make_shared<UInt32Type>();
|
||||
const auto INT32 = std::make_shared<Int32Type>();
|
||||
const auto UINT64 = std::make_shared<UInt64Type>();
|
||||
const auto INT64 = std::make_shared<Int64Type>();
|
||||
const auto FLOAT = std::make_shared<FloatType>();
|
||||
const auto DOUBLE = std::make_shared<DoubleType>();
|
||||
const auto UTF8 = std::make_shared<StringType>();
|
||||
const auto TIMESTAMP_MS = std::make_shared<TimestampType>(TimestampType::Unit::MILLI);
|
||||
const auto BINARY = std::make_shared<ListType>(std::make_shared<Field>("", UINT8));
|
||||
|
||||
TypePtr MakeDecimalType(const PrimitiveNode* node) {
|
||||
int precision = node->decimal_metadata().precision;
|
||||
int scale = node->decimal_metadata().scale;
|
||||
return std::make_shared<DecimalType>(precision, scale);
|
||||
}
|
||||
|
||||
static Status FromByteArray(const PrimitiveNode* node, TypePtr* out) {
|
||||
switch (node->logical_type()) {
|
||||
case LogicalType::UTF8:
|
||||
*out = UTF8;
|
||||
break;
|
||||
case LogicalType::DECIMAL:
|
||||
*out = MakeDecimalType(node);
|
||||
break;
|
||||
default:
|
||||
// BINARY
|
||||
*out = BINARY;
|
||||
break;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status FromFLBA(const PrimitiveNode* node, TypePtr* out) {
|
||||
switch (node->logical_type()) {
|
||||
case LogicalType::NONE:
|
||||
*out = BINARY;
|
||||
break;
|
||||
case LogicalType::DECIMAL:
|
||||
*out = MakeDecimalType(node);
|
||||
break;
|
||||
default:
|
||||
return Status::NotImplemented("unhandled type");
|
||||
break;
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status FromInt32(const PrimitiveNode* node, TypePtr* out) {
|
||||
switch (node->logical_type()) {
|
||||
case LogicalType::NONE:
|
||||
*out = INT32;
|
||||
break;
|
||||
case LogicalType::UINT_8:
|
||||
*out = UINT8;
|
||||
break;
|
||||
case LogicalType::INT_8:
|
||||
*out = INT8;
|
||||
break;
|
||||
case LogicalType::UINT_16:
|
||||
*out = UINT16;
|
||||
break;
|
||||
case LogicalType::INT_16:
|
||||
*out = INT16;
|
||||
break;
|
||||
case LogicalType::UINT_32:
|
||||
*out = UINT32;
|
||||
break;
|
||||
case LogicalType::DECIMAL:
|
||||
*out = MakeDecimalType(node);
|
||||
break;
|
||||
default:
|
||||
return Status::NotImplemented("Unhandled logical type for int32");
|
||||
break;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static Status FromInt64(const PrimitiveNode* node, TypePtr* out) {
|
||||
switch (node->logical_type()) {
|
||||
case LogicalType::NONE:
|
||||
*out = INT64;
|
||||
break;
|
||||
case LogicalType::UINT_64:
|
||||
*out = UINT64;
|
||||
break;
|
||||
case LogicalType::DECIMAL:
|
||||
*out = MakeDecimalType(node);
|
||||
break;
|
||||
case LogicalType::TIMESTAMP_MILLIS:
|
||||
*out = TIMESTAMP_MS;
|
||||
break;
|
||||
default:
|
||||
return Status::NotImplemented("Unhandled logical type for int64");
|
||||
break;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// TODO: Logical Type Handling
|
||||
Status NodeToField(const NodePtr& node, std::shared_ptr<Field>* out) {
|
||||
std::shared_ptr<DataType> type;
|
||||
|
||||
if (node->is_repeated()) {
|
||||
return Status::NotImplemented("No support yet for repeated node types");
|
||||
}
|
||||
|
||||
if (node->is_group()) {
|
||||
const GroupNode* group = static_cast<const GroupNode*>(node.get());
|
||||
std::vector<std::shared_ptr<Field>> fields(group->field_count());
|
||||
for (int i = 0; i < group->field_count(); i++) {
|
||||
RETURN_NOT_OK(NodeToField(group->field(i), &fields[i]));
|
||||
}
|
||||
type = std::make_shared<StructType>(fields);
|
||||
} else {
|
||||
// Primitive (leaf) node
|
||||
const PrimitiveNode* primitive = static_cast<const PrimitiveNode*>(node.get());
|
||||
|
||||
switch (primitive->physical_type()) {
|
||||
case ParquetType::BOOLEAN:
|
||||
type = BOOL;
|
||||
break;
|
||||
case ParquetType::INT32:
|
||||
RETURN_NOT_OK(FromInt32(primitive, &type));
|
||||
break;
|
||||
case ParquetType::INT64:
|
||||
RETURN_NOT_OK(FromInt64(primitive, &type));
|
||||
break;
|
||||
case ParquetType::INT96:
|
||||
// TODO: Do we have that type in Arrow?
|
||||
// type = TypePtr(new Int96Type());
|
||||
return Status::NotImplemented("int96");
|
||||
case ParquetType::FLOAT:
|
||||
type = FLOAT;
|
||||
break;
|
||||
case ParquetType::DOUBLE:
|
||||
type = DOUBLE;
|
||||
break;
|
||||
case ParquetType::BYTE_ARRAY:
|
||||
// TODO: Do we have that type in Arrow?
|
||||
RETURN_NOT_OK(FromByteArray(primitive, &type));
|
||||
break;
|
||||
case ParquetType::FIXED_LEN_BYTE_ARRAY:
|
||||
RETURN_NOT_OK(FromFLBA(primitive, &type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out = std::make_shared<Field>(node->name(), type, !node->is_required());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FromParquetSchema(
|
||||
const ::parquet::SchemaDescriptor* parquet_schema, std::shared_ptr<Schema>* out) {
|
||||
// TODO(wesm): Consider adding an arrow::Schema name attribute, which comes
|
||||
// from the root Parquet node
|
||||
const GroupNode* schema_node =
|
||||
static_cast<const GroupNode*>(parquet_schema->group_node());
|
||||
|
||||
std::vector<std::shared_ptr<Field>> fields(schema_node->field_count());
|
||||
for (int i = 0; i < schema_node->field_count(); i++) {
|
||||
RETURN_NOT_OK(NodeToField(schema_node->field(i), &fields[i]));
|
||||
}
|
||||
|
||||
*out = std::make_shared<Schema>(fields);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status StructToNode(const std::shared_ptr<StructType>& type, const std::string& name,
|
||||
bool nullable, const ::parquet::WriterProperties& properties, NodePtr* out) {
|
||||
Repetition::type repetition = Repetition::REQUIRED;
|
||||
if (nullable) { repetition = Repetition::OPTIONAL; }
|
||||
|
||||
std::vector<NodePtr> children(type->num_children());
|
||||
for (int i = 0; i < type->num_children(); i++) {
|
||||
RETURN_NOT_OK(FieldToNode(type->child(i), properties, &children[i]));
|
||||
}
|
||||
|
||||
*out = GroupNode::Make(name, repetition, children);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FieldToNode(const std::shared_ptr<Field>& field,
|
||||
const ::parquet::WriterProperties& properties, NodePtr* out) {
|
||||
LogicalType::type logical_type = LogicalType::NONE;
|
||||
ParquetType::type type;
|
||||
Repetition::type repetition = Repetition::REQUIRED;
|
||||
if (field->nullable) { repetition = Repetition::OPTIONAL; }
|
||||
int length = -1;
|
||||
|
||||
switch (field->type->type) {
|
||||
// TODO:
|
||||
// case Type::NA:
|
||||
// break;
|
||||
case Type::BOOL:
|
||||
type = ParquetType::BOOLEAN;
|
||||
break;
|
||||
case Type::UINT8:
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::UINT_8;
|
||||
break;
|
||||
case Type::INT8:
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::INT_8;
|
||||
break;
|
||||
case Type::UINT16:
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::UINT_16;
|
||||
break;
|
||||
case Type::INT16:
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::INT_16;
|
||||
break;
|
||||
case Type::UINT32:
|
||||
if (properties.version() == ::parquet::ParquetVersion::PARQUET_1_0) {
|
||||
type = ParquetType::INT64;
|
||||
} else {
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::UINT_32;
|
||||
}
|
||||
break;
|
||||
case Type::INT32:
|
||||
type = ParquetType::INT32;
|
||||
break;
|
||||
case Type::UINT64:
|
||||
type = ParquetType::INT64;
|
||||
logical_type = LogicalType::UINT_64;
|
||||
break;
|
||||
case Type::INT64:
|
||||
type = ParquetType::INT64;
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
type = ParquetType::FLOAT;
|
||||
break;
|
||||
case Type::DOUBLE:
|
||||
type = ParquetType::DOUBLE;
|
||||
break;
|
||||
case Type::STRING:
|
||||
type = ParquetType::BYTE_ARRAY;
|
||||
logical_type = LogicalType::UTF8;
|
||||
break;
|
||||
case Type::BINARY:
|
||||
type = ParquetType::BYTE_ARRAY;
|
||||
break;
|
||||
case Type::DATE:
|
||||
type = ParquetType::INT32;
|
||||
logical_type = LogicalType::DATE;
|
||||
break;
|
||||
case Type::TIMESTAMP: {
|
||||
auto timestamp_type = static_cast<TimestampType*>(field->type.get());
|
||||
if (timestamp_type->unit != TimestampType::Unit::MILLI) {
|
||||
return Status::NotImplemented(
|
||||
"Other timestamp units than millisecond are not yet support with parquet.");
|
||||
}
|
||||
type = ParquetType::INT64;
|
||||
logical_type = LogicalType::TIMESTAMP_MILLIS;
|
||||
} break;
|
||||
case Type::TIMESTAMP_DOUBLE:
|
||||
type = ParquetType::INT64;
|
||||
// This is specified as seconds since the UNIX epoch
|
||||
// TODO: Converted type in Parquet?
|
||||
// logical_type = LogicalType::TIMESTAMP_MILLIS;
|
||||
break;
|
||||
case Type::TIME:
|
||||
type = ParquetType::INT64;
|
||||
logical_type = LogicalType::TIME_MILLIS;
|
||||
break;
|
||||
case Type::STRUCT: {
|
||||
auto struct_type = std::static_pointer_cast<StructType>(field->type);
|
||||
return StructToNode(struct_type, field->name, field->nullable, properties, out);
|
||||
} break;
|
||||
default:
|
||||
// TODO: LIST, DENSE_UNION, SPARE_UNION, JSON_SCALAR, DECIMAL, DECIMAL_TEXT, VARCHAR
|
||||
return Status::NotImplemented("unhandled type");
|
||||
}
|
||||
*out = PrimitiveNode::Make(field->name, repetition, type, logical_type, length);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ToParquetSchema(const Schema* arrow_schema,
|
||||
const ::parquet::WriterProperties& properties,
|
||||
std::shared_ptr<::parquet::SchemaDescriptor>* out) {
|
||||
std::vector<NodePtr> nodes(arrow_schema->num_fields());
|
||||
for (int i = 0; i < arrow_schema->num_fields(); i++) {
|
||||
RETURN_NOT_OK(FieldToNode(arrow_schema->field(i), properties, &nodes[i]));
|
||||
}
|
||||
|
||||
NodePtr schema = GroupNode::Make("schema", Repetition::REPEATED, nodes);
|
||||
*out = std::make_shared<::parquet::SchemaDescriptor>();
|
||||
PARQUET_CATCH_NOT_OK((*out)->Init(schema));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
@@ -1,53 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#ifndef ARROW_PARQUET_SCHEMA_H
|
||||
#define ARROW_PARQUET_SCHEMA_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "parquet/api/schema.h"
|
||||
#include "parquet/api/writer.h"
|
||||
|
||||
#include "arrow/schema.h"
|
||||
#include "arrow/type.h"
|
||||
#include "arrow/util/visibility.h"
|
||||
|
||||
namespace arrow {
|
||||
|
||||
class Status;
|
||||
|
||||
namespace parquet {
|
||||
|
||||
Status ARROW_EXPORT NodeToField(
|
||||
const ::parquet::schema::NodePtr& node, std::shared_ptr<Field>* out);
|
||||
|
||||
Status ARROW_EXPORT FromParquetSchema(
|
||||
const ::parquet::SchemaDescriptor* parquet_schema, std::shared_ptr<Schema>* out);
|
||||
|
||||
Status ARROW_EXPORT FieldToNode(const std::shared_ptr<Field>& field,
|
||||
const ::parquet::WriterProperties& properties, ::parquet::schema::NodePtr* out);
|
||||
|
||||
Status ARROW_EXPORT ToParquetSchema(const Schema* arrow_schema,
|
||||
const ::parquet::WriterProperties& properties,
|
||||
std::shared_ptr<::parquet::SchemaDescriptor>* out);
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
|
||||
#endif // ARROW_PARQUET_SCHEMA_H
|
||||
@@ -1,193 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "arrow/test-util.h"
|
||||
#include "arrow/types/primitive.h"
|
||||
#include "arrow/types/string.h"
|
||||
|
||||
namespace arrow {
|
||||
|
||||
namespace parquet {
|
||||
|
||||
template <typename ArrowType>
|
||||
using is_arrow_float = std::is_floating_point<typename ArrowType::c_type>;
|
||||
|
||||
template <typename ArrowType>
|
||||
using is_arrow_int = std::is_integral<typename ArrowType::c_type>;
|
||||
|
||||
template <typename ArrowType>
|
||||
using is_arrow_string = std::is_same<ArrowType, StringType>;
|
||||
|
||||
template <class ArrowType>
|
||||
typename std::enable_if<is_arrow_float<ArrowType>::value,
|
||||
std::shared_ptr<PrimitiveArray>>::type
|
||||
NonNullArray(size_t size) {
|
||||
std::vector<typename ArrowType::c_type> values;
|
||||
::arrow::test::random_real<typename ArrowType::c_type>(size, 0, 0, 1, &values);
|
||||
NumericBuilder<ArrowType> builder(default_memory_pool(), std::make_shared<ArrowType>());
|
||||
builder.Append(values.data(), values.size());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
template <class ArrowType>
|
||||
typename std::enable_if<is_arrow_int<ArrowType>::value,
|
||||
std::shared_ptr<PrimitiveArray>>::type
|
||||
NonNullArray(size_t size) {
|
||||
std::vector<typename ArrowType::c_type> values;
|
||||
::arrow::test::randint<typename ArrowType::c_type>(size, 0, 64, &values);
|
||||
NumericBuilder<ArrowType> builder(default_memory_pool(), std::make_shared<ArrowType>());
|
||||
builder.Append(values.data(), values.size());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
template <class ArrowType>
|
||||
typename std::enable_if<is_arrow_string<ArrowType>::value,
|
||||
std::shared_ptr<StringArray>>::type
|
||||
NonNullArray(size_t size) {
|
||||
StringBuilder builder(default_memory_pool(), std::make_shared<StringType>());
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
builder.Append("test-string");
|
||||
}
|
||||
return std::static_pointer_cast<StringArray>(builder.Finish());
|
||||
}
|
||||
|
||||
template <>
|
||||
std::shared_ptr<PrimitiveArray> NonNullArray<BooleanType>(size_t size) {
|
||||
std::vector<uint8_t> values;
|
||||
::arrow::test::randint<uint8_t>(size, 0, 1, &values);
|
||||
BooleanBuilder builder(default_memory_pool(), std::make_shared<BooleanType>());
|
||||
builder.Append(values.data(), values.size());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
// This helper function only supports (size/2) nulls.
|
||||
template <typename ArrowType>
|
||||
typename std::enable_if<is_arrow_float<ArrowType>::value,
|
||||
std::shared_ptr<PrimitiveArray>>::type
|
||||
NullableArray(size_t size, size_t num_nulls) {
|
||||
std::vector<typename ArrowType::c_type> values;
|
||||
::arrow::test::random_real<typename ArrowType::c_type>(size, 0, 0, 1, &values);
|
||||
std::vector<uint8_t> valid_bytes(size, 1);
|
||||
|
||||
for (size_t i = 0; i < num_nulls; i++) {
|
||||
valid_bytes[i * 2] = 0;
|
||||
}
|
||||
|
||||
NumericBuilder<ArrowType> builder(default_memory_pool(), std::make_shared<ArrowType>());
|
||||
builder.Append(values.data(), values.size(), valid_bytes.data());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
// This helper function only supports (size/2) nulls.
|
||||
template <typename ArrowType>
|
||||
typename std::enable_if<is_arrow_int<ArrowType>::value,
|
||||
std::shared_ptr<PrimitiveArray>>::type
|
||||
NullableArray(size_t size, size_t num_nulls) {
|
||||
std::vector<typename ArrowType::c_type> values;
|
||||
::arrow::test::randint<typename ArrowType::c_type>(size, 0, 64, &values);
|
||||
std::vector<uint8_t> valid_bytes(size, 1);
|
||||
|
||||
for (size_t i = 0; i < num_nulls; i++) {
|
||||
valid_bytes[i * 2] = 0;
|
||||
}
|
||||
|
||||
NumericBuilder<ArrowType> builder(default_memory_pool(), std::make_shared<ArrowType>());
|
||||
builder.Append(values.data(), values.size(), valid_bytes.data());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
// This helper function only supports (size/2) nulls yet.
|
||||
template <typename ArrowType>
|
||||
typename std::enable_if<is_arrow_string<ArrowType>::value,
|
||||
std::shared_ptr<StringArray>>::type
|
||||
NullableArray(size_t size, size_t num_nulls) {
|
||||
std::vector<uint8_t> valid_bytes(size, 1);
|
||||
|
||||
for (size_t i = 0; i < num_nulls; i++) {
|
||||
valid_bytes[i * 2] = 0;
|
||||
}
|
||||
|
||||
StringBuilder builder(default_memory_pool(), std::make_shared<StringType>());
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
builder.Append("test-string");
|
||||
}
|
||||
return std::static_pointer_cast<StringArray>(builder.Finish());
|
||||
}
|
||||
|
||||
// This helper function only supports (size/2) nulls yet.
|
||||
template <>
|
||||
std::shared_ptr<PrimitiveArray> NullableArray<BooleanType>(
|
||||
size_t size, size_t num_nulls) {
|
||||
std::vector<uint8_t> values;
|
||||
::arrow::test::randint<uint8_t>(size, 0, 1, &values);
|
||||
std::vector<uint8_t> valid_bytes(size, 1);
|
||||
|
||||
for (size_t i = 0; i < num_nulls; i++) {
|
||||
valid_bytes[i * 2] = 0;
|
||||
}
|
||||
|
||||
BooleanBuilder builder(default_memory_pool(), std::make_shared<BooleanType>());
|
||||
builder.Append(values.data(), values.size(), valid_bytes.data());
|
||||
return std::static_pointer_cast<PrimitiveArray>(builder.Finish());
|
||||
}
|
||||
|
||||
std::shared_ptr<Column> MakeColumn(
|
||||
const std::string& name, const std::shared_ptr<Array>& array, bool nullable) {
|
||||
auto field = std::make_shared<Field>(name, array->type(), nullable);
|
||||
return std::make_shared<Column>(field, array);
|
||||
}
|
||||
|
||||
std::shared_ptr<Table> MakeSimpleTable(
|
||||
const std::shared_ptr<Array>& values, bool nullable) {
|
||||
std::shared_ptr<Column> column = MakeColumn("col", values, nullable);
|
||||
std::vector<std::shared_ptr<Column>> columns({column});
|
||||
std::vector<std::shared_ptr<Field>> fields({column->field()});
|
||||
auto schema = std::make_shared<Schema>(fields);
|
||||
return std::make_shared<Table>("table", schema, columns);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ExpectArray(T* expected, Array* result) {
|
||||
PrimitiveArray* p_array = static_cast<PrimitiveArray*>(result);
|
||||
for (int i = 0; i < result->length(); i++) {
|
||||
EXPECT_EQ(expected[i], reinterpret_cast<const T*>(p_array->data()->data())[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ArrowType>
|
||||
void ExpectArray(typename ArrowType::c_type* expected, Array* result) {
|
||||
PrimitiveArray* p_array = static_cast<PrimitiveArray*>(result);
|
||||
for (int64_t i = 0; i < result->length(); i++) {
|
||||
EXPECT_EQ(expected[i],
|
||||
reinterpret_cast<const typename ArrowType::c_type*>(p_array->data()->data())[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void ExpectArray<BooleanType>(uint8_t* expected, Array* result) {
|
||||
BooleanBuilder builder(default_memory_pool(), std::make_shared<BooleanType>());
|
||||
builder.Append(expected, result->length());
|
||||
std::shared_ptr<Array> expected_array = builder.Finish();
|
||||
EXPECT_TRUE(result->Equals(expected_array));
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
@@ -1,52 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#ifndef ARROW_PARQUET_UTILS_H
|
||||
#define ARROW_PARQUET_UTILS_H
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "arrow/util/status.h"
|
||||
#include "parquet/exception.h"
|
||||
|
||||
namespace arrow {
|
||||
namespace parquet {
|
||||
|
||||
#define PARQUET_CATCH_NOT_OK(s) \
|
||||
try { \
|
||||
(s); \
|
||||
} catch (const ::parquet::ParquetException& e) { return Status::Invalid(e.what()); }
|
||||
|
||||
#define PARQUET_IGNORE_NOT_OK(s) \
|
||||
try { \
|
||||
(s); \
|
||||
} catch (const ::parquet::ParquetException& e) {}
|
||||
|
||||
#define PARQUET_THROW_NOT_OK(s) \
|
||||
do { \
|
||||
::arrow::Status _s = (s); \
|
||||
if (!_s.ok()) { \
|
||||
std::stringstream ss; \
|
||||
ss << "Arrow error: " << _s.ToString(); \
|
||||
throw ::parquet::ParquetException(ss.str()); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
} // namespace parquet
|
||||
} // namespace arrow
|
||||
|
||||
#endif // ARROW_PARQUET_UTILS_H
|
||||
@@ -1,365 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#include "arrow/parquet/writer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "arrow/array.h"
|
||||
#include "arrow/column.h"
|
||||
#include "arrow/table.h"
|
||||
#include "arrow/types/construct.h"
|
||||
#include "arrow/types/primitive.h"
|
||||
#include "arrow/types/string.h"
|
||||
#include "arrow/parquet/schema.h"
|
||||
#include "arrow/parquet/utils.h"
|
||||
#include "arrow/util/status.h"
|
||||
|
||||
using parquet::ParquetFileWriter;
|
||||
using parquet::ParquetVersion;
|
||||
using parquet::schema::GroupNode;
|
||||
|
||||
namespace arrow {
|
||||
namespace parquet {
|
||||
|
||||
class FileWriter::Impl {
|
||||
public:
|
||||
Impl(MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileWriter> writer);
|
||||
|
||||
Status NewRowGroup(int64_t chunk_size);
|
||||
template <typename ParquetType, typename ArrowType>
|
||||
Status TypedWriteBatch(::parquet::ColumnWriter* writer, const PrimitiveArray* data,
|
||||
int64_t offset, int64_t length);
|
||||
|
||||
// TODO(uwe): Same code as in reader.cc the only difference is the name of the temporary
|
||||
// buffer
|
||||
template <typename InType, typename OutType>
|
||||
struct can_copy_ptr {
|
||||
static constexpr bool value =
|
||||
std::is_same<InType, OutType>::value ||
|
||||
(std::is_integral<InType>{} && std::is_integral<OutType>{} &&
|
||||
(sizeof(InType) == sizeof(OutType)));
|
||||
};
|
||||
|
||||
template <typename InType, typename OutType,
|
||||
typename std::enable_if<can_copy_ptr<InType, OutType>::value>::type* = nullptr>
|
||||
Status ConvertPhysicalType(const InType* in_ptr, int64_t, const OutType** out_ptr) {
|
||||
*out_ptr = reinterpret_cast<const OutType*>(in_ptr);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename InType, typename OutType,
|
||||
typename std::enable_if<not can_copy_ptr<InType, OutType>::value>::type* = nullptr>
|
||||
Status ConvertPhysicalType(
|
||||
const InType* in_ptr, int64_t length, const OutType** out_ptr) {
|
||||
RETURN_NOT_OK(data_buffer_.Resize(length * sizeof(OutType)));
|
||||
OutType* mutable_out_ptr = reinterpret_cast<OutType*>(data_buffer_.mutable_data());
|
||||
std::copy(in_ptr, in_ptr + length, mutable_out_ptr);
|
||||
*out_ptr = mutable_out_ptr;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status WriteFlatColumnChunk(const PrimitiveArray* data, int64_t offset, int64_t length);
|
||||
Status WriteFlatColumnChunk(const StringArray* data, int64_t offset, int64_t length);
|
||||
Status Close();
|
||||
|
||||
virtual ~Impl() {}
|
||||
|
||||
private:
|
||||
friend class FileWriter;
|
||||
|
||||
MemoryPool* pool_;
|
||||
// Buffer used for storing the data of an array converted to the physical type
|
||||
// as expected by parquet-cpp.
|
||||
PoolBuffer data_buffer_;
|
||||
PoolBuffer def_levels_buffer_;
|
||||
std::unique_ptr<::parquet::ParquetFileWriter> writer_;
|
||||
::parquet::RowGroupWriter* row_group_writer_;
|
||||
};
|
||||
|
||||
FileWriter::Impl::Impl(
|
||||
MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileWriter> writer)
|
||||
: pool_(pool),
|
||||
data_buffer_(pool),
|
||||
writer_(std::move(writer)),
|
||||
row_group_writer_(nullptr) {}
|
||||
|
||||
Status FileWriter::Impl::NewRowGroup(int64_t chunk_size) {
|
||||
if (row_group_writer_ != nullptr) { PARQUET_CATCH_NOT_OK(row_group_writer_->Close()); }
|
||||
PARQUET_CATCH_NOT_OK(row_group_writer_ = writer_->AppendRowGroup(chunk_size));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename ParquetType, typename ArrowType>
|
||||
Status FileWriter::Impl::TypedWriteBatch(::parquet::ColumnWriter* column_writer,
|
||||
const PrimitiveArray* data, int64_t offset, int64_t length) {
|
||||
using ArrowCType = typename ArrowType::c_type;
|
||||
using ParquetCType = typename ParquetType::c_type;
|
||||
|
||||
DCHECK((offset + length) <= data->length());
|
||||
auto data_ptr = reinterpret_cast<const ArrowCType*>(data->data()->data()) + offset;
|
||||
auto writer =
|
||||
reinterpret_cast<::parquet::TypedColumnWriter<ParquetType>*>(column_writer);
|
||||
if (writer->descr()->max_definition_level() == 0) {
|
||||
// no nulls, just dump the data
|
||||
const ParquetCType* data_writer_ptr = nullptr;
|
||||
RETURN_NOT_OK((ConvertPhysicalType<ArrowCType, ParquetCType>(
|
||||
data_ptr, length, &data_writer_ptr)));
|
||||
PARQUET_CATCH_NOT_OK(writer->WriteBatch(length, nullptr, nullptr, data_writer_ptr));
|
||||
} else if (writer->descr()->max_definition_level() == 1) {
|
||||
RETURN_NOT_OK(def_levels_buffer_.Resize(length * sizeof(int16_t)));
|
||||
int16_t* def_levels_ptr =
|
||||
reinterpret_cast<int16_t*>(def_levels_buffer_.mutable_data());
|
||||
if (data->null_count() == 0) {
|
||||
std::fill(def_levels_ptr, def_levels_ptr + length, 1);
|
||||
const ParquetCType* data_writer_ptr = nullptr;
|
||||
RETURN_NOT_OK((ConvertPhysicalType<ArrowCType, ParquetCType>(
|
||||
data_ptr, length, &data_writer_ptr)));
|
||||
PARQUET_CATCH_NOT_OK(
|
||||
writer->WriteBatch(length, def_levels_ptr, nullptr, data_writer_ptr));
|
||||
} else {
|
||||
RETURN_NOT_OK(data_buffer_.Resize(length * sizeof(ParquetCType)));
|
||||
auto buffer_ptr = reinterpret_cast<ParquetCType*>(data_buffer_.mutable_data());
|
||||
int buffer_idx = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (data->IsNull(offset + i)) {
|
||||
def_levels_ptr[i] = 0;
|
||||
} else {
|
||||
def_levels_ptr[i] = 1;
|
||||
buffer_ptr[buffer_idx++] = static_cast<ParquetCType>(data_ptr[i]);
|
||||
}
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(
|
||||
writer->WriteBatch(length, def_levels_ptr, nullptr, buffer_ptr));
|
||||
}
|
||||
} else {
|
||||
return Status::NotImplemented("no support for max definition level > 1 yet");
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->Close());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// This specialization seems quite similar but it significantly differs in two points:
|
||||
// * offset is added at the most latest time to the pointer as we have sub-byte access
|
||||
// * Arrow data is stored bitwise thus we cannot use std::copy to transform from
|
||||
// ArrowType::c_type to ParquetType::c_type
|
||||
template <>
|
||||
Status FileWriter::Impl::TypedWriteBatch<::parquet::BooleanType, BooleanType>(
|
||||
::parquet::ColumnWriter* column_writer, const PrimitiveArray* data, int64_t offset,
|
||||
int64_t length) {
|
||||
DCHECK((offset + length) <= data->length());
|
||||
RETURN_NOT_OK(data_buffer_.Resize(length));
|
||||
auto data_ptr = reinterpret_cast<const uint8_t*>(data->data()->data());
|
||||
auto buffer_ptr = reinterpret_cast<bool*>(data_buffer_.mutable_data());
|
||||
auto writer = reinterpret_cast<::parquet::TypedColumnWriter<::parquet::BooleanType>*>(
|
||||
column_writer);
|
||||
if (writer->descr()->max_definition_level() == 0) {
|
||||
// no nulls, just dump the data
|
||||
for (int64_t i = 0; i < length; i++) {
|
||||
buffer_ptr[i] = util::get_bit(data_ptr, offset + i);
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->WriteBatch(length, nullptr, nullptr, buffer_ptr));
|
||||
} else if (writer->descr()->max_definition_level() == 1) {
|
||||
RETURN_NOT_OK(def_levels_buffer_.Resize(length * sizeof(int16_t)));
|
||||
int16_t* def_levels_ptr =
|
||||
reinterpret_cast<int16_t*>(def_levels_buffer_.mutable_data());
|
||||
if (data->null_count() == 0) {
|
||||
std::fill(def_levels_ptr, def_levels_ptr + length, 1);
|
||||
for (int64_t i = 0; i < length; i++) {
|
||||
buffer_ptr[i] = util::get_bit(data_ptr, offset + i);
|
||||
}
|
||||
// TODO(PARQUET-644): write boolean values as a packed bitmap
|
||||
PARQUET_CATCH_NOT_OK(
|
||||
writer->WriteBatch(length, def_levels_ptr, nullptr, buffer_ptr));
|
||||
} else {
|
||||
int buffer_idx = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (data->IsNull(offset + i)) {
|
||||
def_levels_ptr[i] = 0;
|
||||
} else {
|
||||
def_levels_ptr[i] = 1;
|
||||
buffer_ptr[buffer_idx++] = util::get_bit(data_ptr, offset + i);
|
||||
}
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(
|
||||
writer->WriteBatch(length, def_levels_ptr, nullptr, buffer_ptr));
|
||||
}
|
||||
} else {
|
||||
return Status::NotImplemented("no support for max definition level > 1 yet");
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->Close());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status FileWriter::Impl::Close() {
|
||||
if (row_group_writer_ != nullptr) { PARQUET_CATCH_NOT_OK(row_group_writer_->Close()); }
|
||||
PARQUET_CATCH_NOT_OK(writer_->Close());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#define TYPED_BATCH_CASE(ENUM, ArrowType, ParquetType) \
|
||||
case Type::ENUM: \
|
||||
return TypedWriteBatch<ParquetType, ArrowType>(writer, data, offset, length); \
|
||||
break;
|
||||
|
||||
Status FileWriter::Impl::WriteFlatColumnChunk(
|
||||
const PrimitiveArray* data, int64_t offset, int64_t length) {
|
||||
::parquet::ColumnWriter* writer;
|
||||
PARQUET_CATCH_NOT_OK(writer = row_group_writer_->NextColumn());
|
||||
switch (data->type_enum()) {
|
||||
TYPED_BATCH_CASE(BOOL, BooleanType, ::parquet::BooleanType)
|
||||
TYPED_BATCH_CASE(UINT8, UInt8Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(INT8, Int8Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(UINT16, UInt16Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(INT16, Int16Type, ::parquet::Int32Type)
|
||||
case Type::UINT32:
|
||||
if (writer_->properties()->version() == ParquetVersion::PARQUET_1_0) {
|
||||
// Parquet 1.0 reader cannot read the UINT_32 logical type. Thus we need
|
||||
// to use the larger Int64Type to store them lossless.
|
||||
return TypedWriteBatch<::parquet::Int64Type, UInt32Type>(
|
||||
writer, data, offset, length);
|
||||
} else {
|
||||
return TypedWriteBatch<::parquet::Int32Type, UInt32Type>(
|
||||
writer, data, offset, length);
|
||||
}
|
||||
TYPED_BATCH_CASE(INT32, Int32Type, ::parquet::Int32Type)
|
||||
TYPED_BATCH_CASE(UINT64, UInt64Type, ::parquet::Int64Type)
|
||||
TYPED_BATCH_CASE(INT64, Int64Type, ::parquet::Int64Type)
|
||||
TYPED_BATCH_CASE(TIMESTAMP, TimestampType, ::parquet::Int64Type)
|
||||
TYPED_BATCH_CASE(FLOAT, FloatType, ::parquet::FloatType)
|
||||
TYPED_BATCH_CASE(DOUBLE, DoubleType, ::parquet::DoubleType)
|
||||
default:
|
||||
return Status::NotImplemented(data->type()->ToString());
|
||||
}
|
||||
}
|
||||
|
||||
Status FileWriter::Impl::WriteFlatColumnChunk(
|
||||
const StringArray* data, int64_t offset, int64_t length) {
|
||||
::parquet::ColumnWriter* column_writer;
|
||||
PARQUET_CATCH_NOT_OK(column_writer = row_group_writer_->NextColumn());
|
||||
DCHECK((offset + length) <= data->length());
|
||||
RETURN_NOT_OK(data_buffer_.Resize(length * sizeof(::parquet::ByteArray)));
|
||||
auto buffer_ptr = reinterpret_cast<::parquet::ByteArray*>(data_buffer_.mutable_data());
|
||||
auto values = std::dynamic_pointer_cast<PrimitiveArray>(data->values());
|
||||
auto data_ptr = reinterpret_cast<const uint8_t*>(values->data()->data());
|
||||
DCHECK(values != nullptr);
|
||||
auto writer = reinterpret_cast<::parquet::TypedColumnWriter<::parquet::ByteArrayType>*>(
|
||||
column_writer);
|
||||
if (writer->descr()->max_definition_level() > 0) {
|
||||
RETURN_NOT_OK(def_levels_buffer_.Resize(length * sizeof(int16_t)));
|
||||
}
|
||||
int16_t* def_levels_ptr = reinterpret_cast<int16_t*>(def_levels_buffer_.mutable_data());
|
||||
if (writer->descr()->max_definition_level() == 0 || data->null_count() == 0) {
|
||||
// no nulls, just dump the data
|
||||
for (int64_t i = 0; i < length; i++) {
|
||||
buffer_ptr[i] = ::parquet::ByteArray(
|
||||
data->value_length(i + offset), data_ptr + data->value_offset(i));
|
||||
}
|
||||
if (writer->descr()->max_definition_level() > 0) {
|
||||
std::fill(def_levels_ptr, def_levels_ptr + length, 1);
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->WriteBatch(length, def_levels_ptr, nullptr, buffer_ptr));
|
||||
} else if (writer->descr()->max_definition_level() == 1) {
|
||||
int buffer_idx = 0;
|
||||
for (int64_t i = 0; i < length; i++) {
|
||||
if (data->IsNull(offset + i)) {
|
||||
def_levels_ptr[i] = 0;
|
||||
} else {
|
||||
def_levels_ptr[i] = 1;
|
||||
buffer_ptr[buffer_idx++] = ::parquet::ByteArray(
|
||||
data->value_length(i + offset), data_ptr + data->value_offset(i + offset));
|
||||
}
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->WriteBatch(length, def_levels_ptr, nullptr, buffer_ptr));
|
||||
} else {
|
||||
return Status::NotImplemented("no support for max definition level > 1 yet");
|
||||
}
|
||||
PARQUET_CATCH_NOT_OK(writer->Close());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
FileWriter::FileWriter(
|
||||
MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileWriter> writer)
|
||||
: impl_(new FileWriter::Impl(pool, std::move(writer))) {}
|
||||
|
||||
Status FileWriter::NewRowGroup(int64_t chunk_size) {
|
||||
return impl_->NewRowGroup(chunk_size);
|
||||
}
|
||||
|
||||
Status FileWriter::WriteFlatColumnChunk(
|
||||
const Array* array, int64_t offset, int64_t length) {
|
||||
int64_t real_length = length;
|
||||
if (length == -1) { real_length = array->length(); }
|
||||
if (array->type_enum() == Type::STRING) {
|
||||
auto string_array = dynamic_cast<const StringArray*>(array);
|
||||
DCHECK(string_array);
|
||||
return impl_->WriteFlatColumnChunk(string_array, offset, real_length);
|
||||
} else {
|
||||
auto primitive_array = dynamic_cast<const PrimitiveArray*>(array);
|
||||
if (!primitive_array) {
|
||||
return Status::NotImplemented("Table must consist of PrimitiveArray instances");
|
||||
}
|
||||
return impl_->WriteFlatColumnChunk(primitive_array, offset, real_length);
|
||||
}
|
||||
}
|
||||
|
||||
Status FileWriter::Close() {
|
||||
return impl_->Close();
|
||||
}
|
||||
|
||||
MemoryPool* FileWriter::memory_pool() const {
|
||||
return impl_->pool_;
|
||||
}
|
||||
|
||||
FileWriter::~FileWriter() {}
|
||||
|
||||
Status WriteFlatTable(const Table* table, MemoryPool* pool,
|
||||
const std::shared_ptr<::parquet::OutputStream>& sink, int64_t chunk_size,
|
||||
const std::shared_ptr<::parquet::WriterProperties>& properties) {
|
||||
std::shared_ptr<::parquet::SchemaDescriptor> parquet_schema;
|
||||
RETURN_NOT_OK(
|
||||
ToParquetSchema(table->schema().get(), *properties.get(), &parquet_schema));
|
||||
auto schema_node = std::static_pointer_cast<GroupNode>(parquet_schema->schema_root());
|
||||
std::unique_ptr<ParquetFileWriter> parquet_writer =
|
||||
ParquetFileWriter::Open(sink, schema_node, properties);
|
||||
FileWriter writer(pool, std::move(parquet_writer));
|
||||
|
||||
// TODO(ARROW-232) Support writing chunked arrays.
|
||||
for (int i = 0; i < table->num_columns(); i++) {
|
||||
if (table->column(i)->data()->num_chunks() != 1) {
|
||||
return Status::NotImplemented("No support for writing chunked arrays yet.");
|
||||
}
|
||||
}
|
||||
|
||||
for (int chunk = 0; chunk * chunk_size < table->num_rows(); chunk++) {
|
||||
int64_t offset = chunk * chunk_size;
|
||||
int64_t size = std::min(chunk_size, table->num_rows() - offset);
|
||||
RETURN_NOT_OK_ELSE(writer.NewRowGroup(size), PARQUET_IGNORE_NOT_OK(writer.Close()));
|
||||
for (int i = 0; i < table->num_columns(); i++) {
|
||||
std::shared_ptr<Array> array = table->column(i)->data()->chunk(0);
|
||||
RETURN_NOT_OK_ELSE(writer.WriteFlatColumnChunk(array.get(), offset, size),
|
||||
PARQUET_IGNORE_NOT_OK(writer.Close()));
|
||||
}
|
||||
}
|
||||
|
||||
return writer.Close();
|
||||
}
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
@@ -1,76 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
#ifndef ARROW_PARQUET_WRITER_H
|
||||
#define ARROW_PARQUET_WRITER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "parquet/api/schema.h"
|
||||
#include "parquet/api/writer.h"
|
||||
|
||||
#include "arrow/util/visibility.h"
|
||||
|
||||
namespace arrow {
|
||||
|
||||
class Array;
|
||||
class MemoryPool;
|
||||
class PrimitiveArray;
|
||||
class RecordBatch;
|
||||
class Status;
|
||||
class StringArray;
|
||||
class Table;
|
||||
|
||||
namespace parquet {
|
||||
|
||||
/**
|
||||
* Iterative API:
|
||||
* Start a new RowGroup/Chunk with NewRowGroup
|
||||
* Write column-by-column the whole column chunk
|
||||
*/
|
||||
class ARROW_EXPORT FileWriter {
|
||||
public:
|
||||
FileWriter(MemoryPool* pool, std::unique_ptr<::parquet::ParquetFileWriter> writer);
|
||||
|
||||
Status NewRowGroup(int64_t chunk_size);
|
||||
Status WriteFlatColumnChunk(const Array* data, int64_t offset = 0, int64_t length = -1);
|
||||
Status Close();
|
||||
|
||||
virtual ~FileWriter();
|
||||
|
||||
MemoryPool* memory_pool() const;
|
||||
|
||||
private:
|
||||
class ARROW_NO_EXPORT Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a flat Table to Parquet.
|
||||
*
|
||||
* The table shall only consist of nullable, non-repeated columns of primitive type.
|
||||
*/
|
||||
Status ARROW_EXPORT WriteFlatTable(const Table* table, MemoryPool* pool,
|
||||
const std::shared_ptr<::parquet::OutputStream>& sink, int64_t chunk_size,
|
||||
const std::shared_ptr<::parquet::WriterProperties>& properties =
|
||||
::parquet::default_writer_properties());
|
||||
|
||||
} // namespace parquet
|
||||
|
||||
} // namespace arrow
|
||||
|
||||
#endif // ARROW_PARQUET_WRITER_H
|
||||
@@ -64,7 +64,7 @@ Status StringArray::Validate() const {
|
||||
// This used to be a static member variable of BinaryBuilder, but it can cause
|
||||
// valgrind to report a (spurious?) memory leak when needed in other shared
|
||||
// libraries. The problem came up while adding explicit visibility to libarrow
|
||||
// and libarrow_parquet
|
||||
// and libparquet_arrow
|
||||
static TypePtr kBinaryValueType = TypePtr(new UInt8Type());
|
||||
|
||||
BinaryBuilder::BinaryBuilder(MemoryPool* pool, const TypePtr& type)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# or more cod ntributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
@@ -294,12 +294,12 @@ function(ADD_THIRDPARTY_LIB LIB_NAME)
|
||||
add_library(${LIB_NAME} STATIC IMPORTED)
|
||||
set_target_properties(${LIB_NAME}
|
||||
PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}")
|
||||
message("Added static library dependency ${LIB_NAME}: ${ARG_STATIC_LIB}")
|
||||
message(STATUS "Added static library dependency ${LIB_NAME}: ${ARG_STATIC_LIB}")
|
||||
else()
|
||||
add_library(${LIB_NAME} SHARED IMPORTED)
|
||||
set_target_properties(${LIB_NAME}
|
||||
PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}")
|
||||
message("Added shared library dependency ${LIB_NAME}: ${ARG_SHARED_LIB}")
|
||||
message(STATUS "Added shared library dependency ${LIB_NAME}: ${ARG_SHARED_LIB}")
|
||||
endif()
|
||||
|
||||
if(ARG_DEPS)
|
||||
@@ -443,12 +443,12 @@ set(LINK_LIBS
|
||||
arrow_io
|
||||
)
|
||||
|
||||
if(PARQUET_FOUND AND ARROW_PARQUET_FOUND)
|
||||
ADD_THIRDPARTY_LIB(arrow_parquet
|
||||
SHARED_LIB ${ARROW_PARQUET_SHARED_LIB})
|
||||
if(PARQUET_FOUND AND PARQUET_ARROW_FOUND)
|
||||
ADD_THIRDPARTY_LIB(parquet_arrow
|
||||
SHARED_LIB ${PARQUET_ARROW_SHARED_LIB})
|
||||
set(LINK_LIBS
|
||||
${LINK_LIBS}
|
||||
arrow_parquet)
|
||||
parquet_arrow)
|
||||
set(CYTHON_EXTENSIONS
|
||||
${CYTHON_EXTENSIONS}
|
||||
parquet)
|
||||
|
||||
@@ -42,11 +42,6 @@ find_library(ARROW_LIB_PATH NAMES arrow
|
||||
${ARROW_SEARCH_LIB_PATH}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
find_library(ARROW_PARQUET_LIB_PATH NAMES arrow_parquet
|
||||
PATHS
|
||||
${ARROW_SEARCH_LIB_PATH}
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
find_library(ARROW_IO_LIB_PATH NAMES arrow_io
|
||||
PATHS
|
||||
${ARROW_SEARCH_LIB_PATH}
|
||||
@@ -56,7 +51,6 @@ if (ARROW_INCLUDE_DIR AND ARROW_LIB_PATH)
|
||||
set(ARROW_FOUND TRUE)
|
||||
set(ARROW_LIB_NAME libarrow)
|
||||
set(ARROW_IO_LIB_NAME libarrow_io)
|
||||
set(ARROW_PARQUET_LIB_NAME libarrow_parquet)
|
||||
|
||||
set(ARROW_LIBS ${ARROW_SEARCH_LIB_PATH})
|
||||
set(ARROW_STATIC_LIB ${ARROW_SEARCH_LIB_PATH}/${ARROW_LIB_NAME}.a)
|
||||
@@ -82,20 +76,6 @@ else ()
|
||||
set(ARROW_FOUND FALSE)
|
||||
endif ()
|
||||
|
||||
if(ARROW_PARQUET_LIB_PATH)
|
||||
set(ARROW_PARQUET_FOUND TRUE)
|
||||
set(ARROW_PARQUET_STATIC_LIB ${ARROW_SEARCH_LIB_PATH}/${ARROW_PARQUET_LIB_NAME}.a)
|
||||
set(ARROW_PARQUET_SHARED_LIB ${ARROW_LIBS}/${ARROW_PARQUET_LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
if (NOT Arrow_FIND_QUIETLY)
|
||||
message(STATUS "Found the Arrow Parquet library: ${ARROW_PARQUET_LIB_PATH}")
|
||||
endif ()
|
||||
else()
|
||||
if (NOT Arrow_FIND_QUIETLY)
|
||||
message(STATUS "Could not find Arrow Parquet library")
|
||||
endif()
|
||||
set(ARROW_PARQUET_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
ARROW_INCLUDE_DIR
|
||||
ARROW_LIBS
|
||||
@@ -103,6 +83,4 @@ mark_as_advanced(
|
||||
ARROW_SHARED_LIB
|
||||
ARROW_IO_STATIC_LIB
|
||||
ARROW_IO_SHARED_LIB
|
||||
ARROW_PARQUET_STATIC_LIB
|
||||
ARROW_PARQUET_SHARED_LIB
|
||||
)
|
||||
@@ -44,6 +44,7 @@ cdef extern from "parquet/api/schema.h" namespace "parquet" nogil:
|
||||
cdef cppclass ColumnDescriptor:
|
||||
pass
|
||||
|
||||
|
||||
cdef extern from "parquet/api/reader.h" namespace "parquet" nogil:
|
||||
cdef cppclass ColumnReader:
|
||||
pass
|
||||
@@ -77,6 +78,7 @@ cdef extern from "parquet/api/reader.h" namespace "parquet" nogil:
|
||||
@staticmethod
|
||||
unique_ptr[ParquetFileReader] OpenFile(const c_string& path)
|
||||
|
||||
|
||||
cdef extern from "parquet/api/writer.h" namespace "parquet" nogil:
|
||||
cdef cppclass ParquetOutputStream" parquet::OutputStream":
|
||||
pass
|
||||
@@ -91,7 +93,7 @@ cdef extern from "parquet/api/writer.h" namespace "parquet" nogil:
|
||||
shared_ptr[WriterProperties] build()
|
||||
|
||||
|
||||
cdef extern from "arrow/parquet/io.h" namespace "arrow::parquet" nogil:
|
||||
cdef extern from "parquet/arrow/io.h" namespace "parquet::arrow" nogil:
|
||||
cdef cppclass ParquetAllocator:
|
||||
ParquetAllocator()
|
||||
ParquetAllocator(MemoryPool* pool)
|
||||
@@ -103,7 +105,7 @@ cdef extern from "arrow/parquet/io.h" namespace "arrow::parquet" nogil:
|
||||
Open(const shared_ptr[ReadableFileInterface]& file)
|
||||
|
||||
|
||||
cdef extern from "arrow/parquet/reader.h" namespace "arrow::parquet" nogil:
|
||||
cdef extern from "parquet/arrow/reader.h" namespace "parquet::arrow" nogil:
|
||||
CStatus OpenFile(const shared_ptr[ReadableFileInterface]& file,
|
||||
ParquetAllocator* allocator,
|
||||
unique_ptr[FileReader]* reader)
|
||||
@@ -113,14 +115,14 @@ cdef extern from "arrow/parquet/reader.h" namespace "arrow::parquet" nogil:
|
||||
CStatus ReadFlatTable(shared_ptr[CTable]* out);
|
||||
|
||||
|
||||
cdef extern from "arrow/parquet/schema.h" namespace "arrow::parquet" nogil:
|
||||
cdef extern from "parquet/arrow/schema.h" namespace "parquet::arrow" nogil:
|
||||
CStatus FromParquetSchema(const SchemaDescriptor* parquet_schema,
|
||||
shared_ptr[CSchema]* out)
|
||||
CStatus ToParquetSchema(const CSchema* arrow_schema,
|
||||
shared_ptr[SchemaDescriptor]* out)
|
||||
|
||||
|
||||
cdef extern from "arrow/parquet/writer.h" namespace "arrow::parquet" nogil:
|
||||
cdef extern from "parquet/arrow/writer.h" namespace "parquet::arrow" nogil:
|
||||
cdef CStatus WriteFlatTable(
|
||||
const CTable* table, MemoryPool* pool,
|
||||
const shared_ptr[ParquetOutputStream]& sink,
|
||||
|
||||
Reference in new issue
Block a user