2424#include " gtest/gtest.h"
2525
2626#include < sstream>
27+ #include < arrow/compute/api.h>
2728
2829#include " parquet/api/reader.h"
2930#include " parquet/api/writer.h"
3738
3839#include " arrow/api.h"
3940#include " arrow/test-util.h"
41+ #include " arrow/util/decimal.h"
4042
4143using arrow::Array;
4244using arrow::ArrayVisitor;
4345using arrow::Buffer;
4446using arrow::ChunkedArray;
4547using arrow::Column;
46- using arrow::EncodeArrayToDictionary;
4748using arrow::ListArray;
4849using arrow::PoolBuffer;
4950using arrow::PrimitiveArray;
5051using arrow::Status;
5152using arrow::Table;
5253using arrow::TimeUnit;
5354using arrow::default_memory_pool;
55+ using arrow::compute::DictionaryEncode;
56+ using arrow::compute::FunctionContext;
57+ using arrow::compute::Datum;
5458using arrow::io::BufferReader;
5559
5660using arrow::test::randint;
@@ -68,10 +72,10 @@ using ColumnVector = std::vector<std::shared_ptr<arrow::Column>>;
6872namespace parquet {
6973namespace arrow {
7074
71- const int SMALL_SIZE = 100 ;
72- const int LARGE_SIZE = 10000 ;
75+ static constexpr int SMALL_SIZE = 100 ;
76+ static constexpr int LARGE_SIZE = 10000 ;
7377
74- constexpr uint32_t kDefaultSeed = 0 ;
78+ static constexpr uint32_t kDefaultSeed = 0 ;
7579
7680LogicalType::type get_logical_type (const ::arrow::DataType& type) {
7781 switch (type.id ()) {
@@ -118,6 +122,8 @@ LogicalType::type get_logical_type(const ::arrow::DataType& type) {
118122 static_cast <const ::arrow::DictionaryType&>(type);
119123 return get_logical_type (*dict_type.dictionary ()->type ());
120124 }
125+ case ArrowId::DECIMAL :
126+ return LogicalType::DECIMAL ;
121127 default :
122128 break ;
123129 }
@@ -147,6 +153,7 @@ ParquetType::type get_physical_type(const ::arrow::DataType& type) {
147153 case ArrowId::STRING :
148154 return ParquetType::BYTE_ARRAY ;
149155 case ArrowId::FIXED_SIZE_BINARY :
156+ case ArrowId::DECIMAL :
150157 return ParquetType::FIXED_LEN_BYTE_ARRAY ;
151158 case ArrowId::DATE32 :
152159 return ParquetType::INT32 ;
@@ -299,6 +306,7 @@ struct test_traits<::arrow::FixedSizeBinaryType> {
299306const std::string test_traits<::arrow::StringType>::value(" Test" ); // NOLINT
300307const std::string test_traits<::arrow::BinaryType>::value(" \x00\x01\x02\x03 " ); // NOLINT
301308const std::string test_traits<::arrow::FixedSizeBinaryType>::value(" Fixed" ); // NOLINT
309+
302310template <typename T>
303311using ParquetDataType = DataType<test_traits<T>::parquet_enum>;
304312
@@ -342,36 +350,52 @@ void DoSimpleRoundtrip(const std::shared_ptr<Table>& table, int num_threads,
342350
343351static std::shared_ptr<GroupNode> MakeSimpleSchema (const ::arrow::DataType& type,
344352 Repetition::type repetition) {
345- int byte_width;
346- // Decimal is not implemented yet.
353+ int32_t byte_width = -1 ;
354+ int32_t precision = -1 ;
355+ int32_t scale = -1 ;
356+
347357 switch (type.id ()) {
348358 case ::arrow::Type::DICTIONARY : {
349- const ::arrow::DictionaryType& dict_type =
350- static_cast <const ::arrow::DictionaryType&>(type);
359+ const auto & dict_type = static_cast <const ::arrow::DictionaryType&>(type);
351360 const ::arrow::DataType& values_type = *dict_type.dictionary ()->type ();
352- if (values_type.id () == ::arrow::Type::FIXED_SIZE_BINARY ) {
353- byte_width =
354- static_cast <const ::arrow::FixedSizeBinaryType&>(values_type).byte_width ();
355- } else {
356- byte_width = -1 ;
361+ switch (values_type.id ()) {
362+ case ::arrow::Type::FIXED_SIZE_BINARY :
363+ byte_width =
364+ static_cast <const ::arrow::FixedSizeBinaryType&>(values_type).byte_width ();
365+ break ;
366+ case ::arrow::Type::DECIMAL : {
367+ const auto & decimal_type =
368+ static_cast <const ::arrow::Decimal128Type&>(values_type);
369+ precision = decimal_type.precision ();
370+ scale = decimal_type.scale ();
371+ byte_width = DecimalSize (precision);
372+ } break ;
373+ default :
374+ break ;
357375 }
358376 } break ;
359377 case ::arrow::Type::FIXED_SIZE_BINARY :
360378 byte_width = static_cast <const ::arrow::FixedSizeBinaryType&>(type).byte_width ();
361379 break ;
380+ case ::arrow::Type::DECIMAL : {
381+ const auto & decimal_type = static_cast <const ::arrow::Decimal128Type&>(type);
382+ precision = decimal_type.precision ();
383+ scale = decimal_type.scale ();
384+ byte_width = DecimalSize (precision);
385+ } break ;
362386 default :
363- byte_width = - 1 ;
387+ break ;
364388 }
365389 auto pnode = PrimitiveNode::Make (" column1" , repetition, get_physical_type (type),
366- get_logical_type (type), byte_width);
390+ get_logical_type (type), byte_width, precision, scale );
367391 NodePtr node_ =
368392 GroupNode::Make (" schema" , Repetition::REQUIRED , std::vector<NodePtr>({pnode}));
369393 return std::static_pointer_cast<GroupNode>(node_);
370394}
371395
372396namespace internal {
373397
374- void AssertArraysEqual (const Array & expected, const Array & actual) {
398+ void AssertArraysEqual (const Array& expected, const Array& actual) {
375399 if (!actual.Equals (expected)) {
376400 std::stringstream pp_result;
377401 std::stringstream pp_expected;
@@ -526,11 +550,19 @@ class TestParquetIO : public ::testing::Test {
526550// There we write an UInt32 Array but receive an Int64 Array as result for
527551// Parquet version 1.0.
528552
529- typedef ::testing::Types<::arrow::BooleanType, ::arrow::UInt8Type, ::arrow::Int8Type,
530- ::arrow::UInt16Type, ::arrow::Int16Type, ::arrow::Int32Type,
531- ::arrow::UInt64Type, ::arrow::Int64Type, ::arrow::Date32Type,
532- ::arrow::FloatType, ::arrow::DoubleType, ::arrow::StringType,
533- ::arrow::BinaryType, ::arrow::FixedSizeBinaryType>
553+ typedef ::testing::Types<
554+ ::arrow::BooleanType, ::arrow::UInt8Type, ::arrow::Int8Type, ::arrow::UInt16Type,
555+ ::arrow::Int16Type, ::arrow::Int32Type, ::arrow::UInt64Type, ::arrow::Int64Type,
556+ ::arrow::Date32Type, ::arrow::FloatType, ::arrow::DoubleType, ::arrow::StringType,
557+ ::arrow::BinaryType, ::arrow::FixedSizeBinaryType, DecimalWithPrecisionAndScale<1 >,
558+ DecimalWithPrecisionAndScale<3 >, DecimalWithPrecisionAndScale<5 >,
559+ DecimalWithPrecisionAndScale<7 >, DecimalWithPrecisionAndScale<10 >,
560+ DecimalWithPrecisionAndScale<12 >, DecimalWithPrecisionAndScale<15 >,
561+ DecimalWithPrecisionAndScale<17 >, DecimalWithPrecisionAndScale<19 >,
562+ DecimalWithPrecisionAndScale<22 >, DecimalWithPrecisionAndScale<23 >,
563+ DecimalWithPrecisionAndScale<24 >, DecimalWithPrecisionAndScale<27 >,
564+ DecimalWithPrecisionAndScale<29 >, DecimalWithPrecisionAndScale<32 >,
565+ DecimalWithPrecisionAndScale<34 >, DecimalWithPrecisionAndScale<38 >>
534566 TestTypes;
535567
536568TYPED_TEST_CASE (TestParquetIO, TestTypes);
@@ -590,8 +622,10 @@ TYPED_TEST(TestParquetIO, SingleColumnOptionalDictionaryWrite) {
590622
591623 ASSERT_OK (NullableArray<TypeParam>(SMALL_SIZE , 10 , kDefaultSeed , &values));
592624
593- std::shared_ptr<Array> dict_values;
594- ASSERT_OK (EncodeArrayToDictionary (*values, default_memory_pool (), &dict_values));
625+ Datum out;
626+ FunctionContext ctx (default_memory_pool ());
627+ ASSERT_OK (DictionaryEncode (&ctx, Datum (values), &out));
628+ std::shared_ptr<Array> dict_values = MakeArray (out.array ());
595629 std::shared_ptr<GroupNode> schema =
596630 MakeSimpleSchema (*dict_values->type (), Repetition::OPTIONAL );
597631 this ->WriteColumn (schema, dict_values);
@@ -856,25 +890,43 @@ TEST_F(TestUInt32ParquetIO, Parquet_1_0_Compability) {
856890 ASSERT_OK_NO_THROW (
857891 WriteTable (*table, ::arrow::default_memory_pool (), this ->sink_ , 512 , properties));
858892
859- std::shared_ptr<Array> expected_values;
860893 std::shared_ptr<PoolBuffer> int64_data =
861894 std::make_shared<PoolBuffer>(::arrow::default_memory_pool ());
862895 {
863896 ASSERT_OK (int64_data->Resize (sizeof (int64_t ) * values->length ()));
864- int64_t * int64_data_ptr = reinterpret_cast <int64_t *>(int64_data->mutable_data ());
865- const uint32_t * uint32_data_ptr =
866- reinterpret_cast < const uint32_t *>(values-> values ()-> data ());
867- // std::copy might be faster but this is explicit on the casts)
868- for ( int64_t i = 0 ; i < values-> length (); i++) {
869- int64_data_ptr[i] = static_cast < int64_t >(uint32_data_ptr[i]);
870- }
897+ auto int64_data_ptr = reinterpret_cast <int64_t *>(int64_data->mutable_data ());
898+ auto uint32_data_ptr = reinterpret_cast < const uint32_t *>(values-> values ()-> data ());
899+ const auto cast_uint32_to_int64 = []( uint32_t value) {
900+ return static_cast < int64_t >(value);
901+ };
902+ std::transform (uint32_data_ptr, uint32_data_ptr + values-> length (), int64_data_ptr,
903+ cast_uint32_to_int64);
871904 }
872905
873906 std::vector<std::shared_ptr<Buffer>> buffers{values->null_bitmap (), int64_data};
874907 auto arr_data = std::make_shared<::arrow::ArrayData>(::arrow::int64 (), values->length (),
875908 buffers, values->null_count ());
876- ASSERT_OK (MakeArray (arr_data, &expected_values));
877- this ->ReadAndCheckSingleColumnTable (expected_values);
909+ std::shared_ptr<Array> expected_values = MakeArray (arr_data);
910+ ASSERT_NE (expected_values, NULLPTR );
911+
912+ const auto & expected = static_cast <const ::arrow::Int64Array&>(*expected_values);
913+ ASSERT_GT (values->length (), 0 );
914+ ASSERT_EQ (values->length (), expected.length ());
915+
916+ // TODO(phillipc): Is there a better way to compare these two arrays?
917+ // AssertArraysEqual requires the same type, but we only care about values in this case
918+ for (int i = 0 ; i < expected.length (); ++i) {
919+ const bool value_is_valid = values->IsValid (i);
920+ const bool expected_value_is_valid = expected.IsValid (i);
921+
922+ ASSERT_EQ (expected_value_is_valid, value_is_valid);
923+
924+ if (value_is_valid) {
925+ uint32_t value = values->Value (i);
926+ int64_t expected_value = expected.Value (i);
927+ ASSERT_EQ (expected_value, static_cast <int64_t >(value));
928+ }
929+ }
878930}
879931
880932using TestStringParquetIO = TestParquetIO<::arrow::StringType>;
@@ -1432,7 +1484,7 @@ void MakeListTable(int num_rows, std::shared_ptr<Table>* out) {
14321484 offset_values.push_back (total_elements);
14331485
14341486 std::vector<int8_t > value_draws;
1435- randint< int8_t > (total_elements, 0 , 100 , &value_draws);
1487+ randint (total_elements, 0 , 100 , &value_draws);
14361488
14371489 std::vector<bool > is_valid;
14381490 random_is_valid (total_elements, 0.1 , &is_valid);
@@ -1889,6 +1941,61 @@ TEST(TestArrowReaderAdHoc, Int96BadMemoryAccess) {
18891941 ASSERT_OK_NO_THROW (arrow_reader->ReadTable (&table));
18901942}
18911943
1944+ class TestArrowReaderAdHocSpark
1945+ : public ::testing::TestWithParam<
1946+ std::tuple<std::string, std::shared_ptr<::arrow::DataType>>> {};
1947+
1948+ TEST_P (TestArrowReaderAdHocSpark, ReadDecimals) {
1949+ std::string path (std::getenv (" PARQUET_TEST_DATA" ));
1950+
1951+ std::string filename;
1952+ std::shared_ptr<::arrow::DataType> decimal_type;
1953+ std::tie (filename, decimal_type) = GetParam ();
1954+
1955+ path += " /" + filename;
1956+ ASSERT_GT (path.size (), 0 );
1957+
1958+ auto pool = ::arrow::default_memory_pool ();
1959+
1960+ std::unique_ptr<FileReader> arrow_reader;
1961+ ASSERT_NO_THROW (
1962+ arrow_reader.reset (new FileReader (pool, ParquetFileReader::OpenFile (path, false ))));
1963+ std::shared_ptr<::arrow::Table> table;
1964+ ASSERT_OK_NO_THROW (arrow_reader->ReadTable (&table));
1965+
1966+ ASSERT_EQ (1 , table->num_columns ());
1967+
1968+ constexpr int32_t expected_length = 24 ;
1969+
1970+ auto value_column = table->column (0 );
1971+ ASSERT_EQ (expected_length, value_column->length ());
1972+
1973+ auto raw_array = value_column->data ();
1974+ ASSERT_EQ (1 , raw_array->num_chunks ());
1975+
1976+ auto chunk = raw_array->chunk (0 );
1977+
1978+ std::shared_ptr<Array> expected_array;
1979+
1980+ ::arrow::Decimal128Builder builder (decimal_type, pool);
1981+
1982+ for (int32_t i = 0 ; i < expected_length; ++i) {
1983+ ::arrow::Decimal128 value ((i + 1 ) * 100 );
1984+ ASSERT_OK (builder.Append (value));
1985+ }
1986+ ASSERT_OK (builder.Finish (&expected_array));
1987+
1988+ internal::AssertArraysEqual (*expected_array, *chunk);
1989+ }
1990+
1991+ INSTANTIATE_TEST_CASE_P (
1992+ ReadDecimals, TestArrowReaderAdHocSpark,
1993+ ::testing::Values (
1994+ std::make_tuple (" int32_decimal.parquet" , ::arrow::decimal(4 , 2 )),
1995+ std::make_tuple(" int64_decimal.parquet" , ::arrow::decimal(10 , 2 )),
1996+ std::make_tuple(" fixed_length_decimal.parquet" , ::arrow::decimal(25 , 2 )),
1997+ std::make_tuple(" fixed_length_decimal_legacy.parquet" , ::arrow::decimal(13 , 2 ))));
1998+
18921999} // namespace arrow
18932000
18942001} // namespace parquet
0 commit comments