| /* | |
| * 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 "paimon/common/utils/projected_array.h" | |
| #include <algorithm> | |
| #include <optional> | |
| #include <string> | |
| #include "gtest/gtest.h" | |
| #include "paimon/common/data/binary_array.h" | |
| #include "paimon/common/data/binary_array_writer.h" | |
| #include "paimon/common/data/binary_map.h" | |
| #include "paimon/common/data/binary_row.h" | |
| #include "paimon/memory/bytes.h" | |
| #include "paimon/memory/memory_pool.h" | |
| #include "paimon/testing/utils/binary_row_generator.h" | |
| namespace paimon::test { | |
| TEST(ProjectedArrayTest, TestSimple) { | |
| auto pool = GetDefaultPool(); | |
| { | |
| std::vector<bool> arr = {true, false}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(bool), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteBoolean(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetBoolean(1), true); | |
| ASSERT_EQ(projected_array.GetBoolean(0), false); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| { | |
| std::vector<int8_t> arr = {1, 2, 3, 4, 5}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(int8_t), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteByte(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {4, 3, 2, 1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetByte(0), 5); | |
| ASSERT_EQ(projected_array.GetByte(1), 4); | |
| ASSERT_EQ(projected_array.GetByte(4), 1); | |
| ASSERT_TRUE(projected_array.IsNullAt(5)); | |
| } | |
| { | |
| std::vector<int16_t> arr = {1, 2, 3, 4, 5}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(int16_t), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteShort(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {4, 3, 2, 1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetShort(0), 5); | |
| ASSERT_EQ(projected_array.GetShort(1), 4); | |
| ASSERT_EQ(projected_array.GetShort(4), 1); | |
| ASSERT_TRUE(projected_array.IsNullAt(5)); | |
| } | |
| { | |
| std::vector<int32_t> arr = {1, 2, 3, 4, 5}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(int32_t), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteInt(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {4, 3, 2, 1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetInt(0), 5); | |
| ASSERT_EQ(projected_array.GetInt(1), 4); | |
| ASSERT_EQ(projected_array.GetInt(4), 1); | |
| ASSERT_TRUE(projected_array.IsNullAt(5)); | |
| } | |
| { | |
| // test date | |
| std::vector<int32_t> arr = {10000, 20006}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(int32_t), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteInt(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetDate(0), 20006); | |
| ASSERT_EQ(projected_array.GetDate(1), 10000); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| { | |
| std::vector<float> arr = {1.0, 2.0}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(float), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteFloat(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetFloat(0), 2.0); | |
| ASSERT_EQ(projected_array.GetFloat(1), 1.0); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| { | |
| std::vector<double> arr = {1.0, 2.0}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), sizeof(double), pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteDouble(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetDouble(0), 2.0); | |
| ASSERT_EQ(projected_array.GetDouble(1), 1.0); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| // decimal | |
| { | |
| // not compact (precision <= 18) | |
| std::vector<Decimal> arr = {Decimal(6, 2, 123456), Decimal(6, 3, 123456)}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteDecimal(i, arr[i], 6); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetDecimal(0, 6, 3), Decimal(6, 3, 123456)); | |
| ASSERT_EQ(projected_array.GetDecimal(1, 6, 2), Decimal(6, 2, 123456)); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| { | |
| // compact (precision > 18) | |
| std::vector<Decimal> arr = {Decimal(/*precision=*/20, /*scale=*/3, 123456), | |
| Decimal(/*precision=*/20, /*scale=*/3, 123456789)}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteDecimal(i, arr[i], /*precision=*/20); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetDecimal(0, 20, 3), Decimal(20, 3, 123456789)); | |
| ASSERT_EQ(projected_array.GetDecimal(1, 20, 3), Decimal(20, 3, 123456)); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| // timestamp | |
| { | |
| std::vector<Timestamp> arr = {Timestamp(0, 0), Timestamp(12345, 1)}; | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteTimestamp(i, arr[i], 9); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetTimestamp(0, 9), Timestamp(12345, 1)); | |
| ASSERT_EQ(projected_array.GetTimestamp(1, 9), Timestamp(0, 0)); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| // binary | |
| { | |
| std::vector<Bytes> arr; | |
| arr.emplace_back("hello", pool.get()); | |
| arr.emplace_back("world", pool.get()); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteBinary(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(*projected_array.GetBinary(0), arr[1]); | |
| ASSERT_EQ(*projected_array.GetBinary(1), arr[0]); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| // string | |
| { | |
| std::vector<BinaryString> arr; | |
| arr.push_back(BinaryString::FromString("hello", pool.get())); | |
| arr.push_back(BinaryString::FromString("world", pool.get())); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteString(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetString(0), arr[1]); | |
| ASSERT_EQ(projected_array.GetString(1), arr[0]); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| // array | |
| { | |
| // element1 | |
| std::vector<int32_t> arr1 = {1, 2}; | |
| auto array1 = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer1 = | |
| BinaryArrayWriter(array1.get(), arr1.size(), sizeof(int32_t), pool.get()); | |
| for (size_t i = 0; i < arr1.size(); i++) { | |
| writer1.WriteInt(i, arr1[i]); | |
| } | |
| writer1.Complete(); | |
| // element2 | |
| std::vector<int32_t> arr2 = {100, 200}; | |
| auto array2 = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer2 = | |
| BinaryArrayWriter(array2.get(), arr2.size(), sizeof(int32_t), pool.get()); | |
| for (size_t i = 0; i < arr2.size(); i++) { | |
| writer2.WriteInt(i, arr2[i]); | |
| } | |
| writer2.Complete(); | |
| // array | |
| std::vector<std::shared_ptr<BinaryArray>> arr; | |
| arr.push_back(array1); | |
| arr.push_back(array2); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = BinaryArrayWriter(array.get(), arr.size(), 8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteArray(i, *arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| auto ret0 = std::dynamic_pointer_cast<BinaryArray>(projected_array.GetArray(0)); | |
| auto ret1 = std::dynamic_pointer_cast<BinaryArray>(projected_array.GetArray(1)); | |
| ASSERT_TRUE(ret0); | |
| ASSERT_TRUE(ret1); | |
| ASSERT_EQ(*ret0, *arr[1]); | |
| ASSERT_EQ(*ret1, *arr[0]); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| } | |
| TEST(ProjectedArrayTest, TestGetStringView) { | |
| auto pool = GetDefaultPool(); | |
| std::vector<BinaryString> arr; | |
| arr.push_back(BinaryString::FromString("hello", pool.get())); | |
| arr.push_back(BinaryString::FromString("world", pool.get())); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), arr.size(), /*element_size=*/8, pool.get()); | |
| for (size_t i = 0; i < arr.size(); i++) { | |
| writer.WriteString(i, arr[i]); | |
| } | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| ASSERT_EQ(projected_array.GetStringView(0), "world"); | |
| ASSERT_EQ(projected_array.GetStringView(1), "hello"); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| TEST(ProjectedArrayTest, TestGetMap) { | |
| auto pool = GetDefaultPool(); | |
| auto key = BinaryArray::FromIntArray({1, 2, 3}, pool.get()); | |
| auto value = BinaryArray::FromLongArray({100ll, 200ll, 300ll}, pool.get()); | |
| auto map1 = BinaryMap::ValueOf(key, value, pool.get()); | |
| auto key2 = BinaryArray::FromIntArray({10, 20}, pool.get()); | |
| auto value2 = BinaryArray::FromLongArray({1000ll, 2000ll}, pool.get()); | |
| auto map2 = BinaryMap::ValueOf(key2, value2, pool.get()); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), /*num_elements=*/2, /*element_size=*/8, pool.get()); | |
| writer.WriteMap(0, *map1); | |
| writer.WriteMap(1, *map2); | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| // mapping[0] -> original index 1 -> map2 | |
| auto result0 = projected_array.GetMap(0); | |
| ASSERT_EQ(result0->Size(), 2); | |
| ASSERT_EQ(result0->KeyArray()->ToIntArray().value(), (std::vector<int32_t>{10, 20})); | |
| // mapping[1] -> original index 0 -> map1 | |
| auto result1 = projected_array.GetMap(1); | |
| ASSERT_EQ(result1->Size(), 3); | |
| ASSERT_EQ(result1->KeyArray()->ToIntArray().value(), (std::vector<int32_t>{1, 2, 3})); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| TEST(ProjectedArrayTest, TestGetRow) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row1 = BinaryRowGenerator::GenerateRow({std::string("Alice"), 30}, pool.get()); | |
| BinaryRow row2 = BinaryRowGenerator::GenerateRow({std::string("Bob"), 40}, pool.get()); | |
| auto array = std::make_shared<BinaryArray>(); | |
| BinaryArrayWriter writer = | |
| BinaryArrayWriter(array.get(), /*num_elements=*/2, /*element_size=*/8, pool.get()); | |
| writer.WriteRow(0, row1); | |
| writer.WriteRow(1, row2); | |
| writer.Complete(); | |
| std::vector<int32_t> mapping = {1, 0, -1}; | |
| ProjectedArray projected_array(array, mapping); | |
| // mapping[0] -> original index 1 -> row2 | |
| auto result0 = std::dynamic_pointer_cast<BinaryRow>(projected_array.GetRow(0, 2)); | |
| ASSERT_TRUE(result0); | |
| ASSERT_EQ(*result0, row2); | |
| // mapping[1] -> original index 0 -> row1 | |
| auto result1 = std::dynamic_pointer_cast<BinaryRow>(projected_array.GetRow(1, 2)); | |
| ASSERT_TRUE(result1); | |
| ASSERT_EQ(*result1, row1); | |
| ASSERT_TRUE(projected_array.IsNullAt(2)); | |
| } | |
| } // namespace paimon::test |