| /* | |
| * 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/reader/data_evolution_row.h" | |
| #include <cstdint> | |
| #include <memory> | |
| #include <string> | |
| #include <variant> | |
| #include <vector> | |
| #include "gtest/gtest.h" | |
| #include "paimon/common/data/binary_array_writer.h" | |
| #include "paimon/common/data/binary_map.h" | |
| #include "paimon/common/data/binary_row_writer.h" | |
| #include "paimon/common/data/data_define.h" | |
| #include "paimon/common/types/row_kind.h" | |
| #include "paimon/data/decimal.h" | |
| #include "paimon/data/timestamp.h" | |
| #include "paimon/memory/bytes.h" | |
| #include "paimon/memory/memory_pool.h" | |
| #include "paimon/status.h" | |
| #include "paimon/testing/utils/binary_row_generator.h" | |
| #include "paimon/testing/utils/testharness.h" | |
| namespace paimon::test { | |
| TEST(DataEvolutionRowTest, TestSimple) { | |
| // f0:int32 | |
| // f1:int32 | |
| // f2:string | |
| // f3:int32 | |
| // f4:string | |
| // f5:int32 | |
| std::vector<int32_t> row_offsets = {0, 2, 0, 1, 2, 1}; | |
| std::vector<int32_t> field_offsets = {0, 0, 1, 1, 1, 0}; | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row1 = BinaryRowGenerator::GenerateRow({0, std::string("00")}, pool.get()); | |
| BinaryRow row2 = BinaryRowGenerator::GenerateRow({10, 110}, pool.get()); | |
| BinaryRow row3 = BinaryRowGenerator::GenerateRow({20, std::string("20")}, pool.get()); | |
| DataEvolutionRow row({row1, row2, row3}, row_offsets, field_offsets); | |
| ASSERT_EQ(row.GetFieldCount(), 6); | |
| ASSERT_FALSE(row.IsNullAt(0)); | |
| ASSERT_EQ(row.GetInt(0), 0); | |
| ASSERT_EQ(row.GetInt(1), 20); | |
| ASSERT_EQ(row.GetString(2).ToString(), "00"); | |
| ASSERT_EQ(row.GetInt(3), 110); | |
| ASSERT_EQ(row.GetString(4).ToString(), "20"); | |
| ASSERT_EQ(row.GetInt(5), 10); | |
| // test set and get row kind | |
| ASSERT_OK_AND_ASSIGN(const RowKind* row_kind, row.GetRowKind()); | |
| ASSERT_EQ(*row_kind, *RowKind::Insert()); | |
| row.SetRowKind(RowKind::UpdateAfter()); | |
| ASSERT_OK_AND_ASSIGN(const RowKind* new_row_kind, row.GetRowKind()); | |
| ASSERT_EQ(*new_row_kind, *RowKind::UpdateAfter()); | |
| ASSERT_EQ(row.ToString(), "DataEvolutionRow"); | |
| } | |
| TEST(DataEvolutionRowTest, TestNull) { | |
| // f0:int32 | |
| // f1:int32 | |
| // f2:string | |
| // f3:int32 | |
| // f4:string | |
| // f5:int32 | |
| // f6:non-exist | |
| std::vector<int32_t> row_offsets = {0, 2, 0, 1, 2, 1, -1, -2}; | |
| std::vector<int32_t> field_offsets = {0, 0, 1, 1, 1, 0, -1, -1}; | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row1 = BinaryRowGenerator::GenerateRow({0, std::string("00")}, pool.get()); | |
| BinaryRow row2 = BinaryRowGenerator::GenerateRow({10, 110}, pool.get()); | |
| BinaryRow row3 = BinaryRowGenerator::GenerateRow({20, NullType()}, pool.get()); | |
| DataEvolutionRow row({row1, row2, row3}, row_offsets, field_offsets); | |
| ASSERT_EQ(row.GetFieldCount(), 8); | |
| ASSERT_FALSE(row.IsNullAt(0)); | |
| ASSERT_EQ(row.GetInt(0), 0); | |
| ASSERT_EQ(row.GetInt(1), 20); | |
| ASSERT_EQ(row.GetString(2).ToString(), "00"); | |
| ASSERT_EQ(row.GetInt(3), 110); | |
| ASSERT_TRUE(row.IsNullAt(4)); | |
| ASSERT_EQ(row.GetInt(5), 10); | |
| ASSERT_TRUE(row.IsNullAt(6)); | |
| ASSERT_TRUE(row.IsNullAt(7)); | |
| } | |
| TEST(DataEvolutionRowTest, TestAllFieldTypes) { | |
| auto pool = GetDefaultPool(); | |
| // Row0: boolean(true), byte(42), short(1024), int(100000), date(19000) | |
| BinaryRow row0 = | |
| BinaryRowGenerator::GenerateRow({true, static_cast<int8_t>(42), static_cast<int16_t>(1024), | |
| static_cast<int32_t>(100000), static_cast<int32_t>(19000)}, | |
| pool.get()); | |
| // Row1: long(-987654321), float(3.14f), double(-9.87654321) | |
| BinaryRow row1 = BinaryRowGenerator::GenerateRow( | |
| {static_cast<int64_t>(-987654321LL), static_cast<float>(3.14f), | |
| static_cast<double>(-9.87654321)}, | |
| pool.get()); | |
| // Row2: string("hello"), string("world") | |
| BinaryRow row2 = | |
| BinaryRowGenerator::GenerateRow({std::string("hello"), std::string("world")}, pool.get()); | |
| // Row3: decimal(10,2,67890), timestamp(1000,500) with precision 9 | |
| BinaryRow row3 = BinaryRowGenerator::GenerateRow( | |
| {Decimal(10, 2, 67890), TimestampType(Timestamp(1000, 500), 9)}, pool.get()); | |
| // Row4: binary("fghij") | |
| auto bytes_val = std::make_shared<Bytes>("fghij", pool.get()); | |
| BinaryRow row4 = BinaryRowGenerator::GenerateRow({bytes_val}, pool.get()); | |
| // Row5: nested array [10,20], nested map {1:100, 2:200}, nested row ("Alice", 30) | |
| // Built manually via BinaryRowWriter since BinaryRowGenerator doesn't support nested types | |
| BinaryRow row5(3); | |
| { | |
| BinaryRowWriter writer(&row5, 0, pool.get()); | |
| // field 0: array [10, 20] | |
| auto inner_array = BinaryArray::FromIntArray({10, 20}, pool.get()); | |
| writer.WriteArray(0, inner_array); | |
| // field 1: map {1->100, 2->200} | |
| auto map_key = BinaryArray::FromIntArray({1, 2}, pool.get()); | |
| auto map_val = BinaryArray::FromLongArray({100LL, 200LL}, pool.get()); | |
| auto map_obj = BinaryMap::ValueOf(map_key, map_val, pool.get()); | |
| writer.WriteMap(1, *map_obj); | |
| // field 2: row ("Alice", 30) | |
| BinaryRow inner_row = BinaryRowGenerator::GenerateRow( | |
| {std::string("Alice"), static_cast<int32_t>(30)}, pool.get()); | |
| writer.WriteRow(2, inner_row); | |
| writer.Complete(); | |
| } | |
| // Build the DataEvolutionRow that picks specific fields from each row. | |
| // evolution field -> (row_index, field_index_within_row) | |
| // pos 0: boolean -> row0[0] = true | |
| // pos 1: byte -> row0[1] = 42 | |
| // pos 2: short -> row0[2] = 1024 | |
| // pos 3: int -> row0[3] = 100000 | |
| // pos 4: date -> row0[4] = 19000 | |
| // pos 5: long -> row1[0] = -987654321 | |
| // pos 6: float -> row1[1] = 3.14f | |
| // pos 7: double -> row1[2] = -9.87654321 | |
| // pos 8: string -> row2[0] = "hello" | |
| // pos 9: stringview-> row2[1] = "world" | |
| // pos 10: decimal -> row3[0] = Decimal(10,2,67890) | |
| // pos 11: timestamp -> row3[1] = Timestamp(1000,500) | |
| // pos 12: binary -> row4[0] = "fghij" | |
| // pos 13: array -> row5[0] = [10,20] | |
| // pos 14: map -> row5[1] = {1:100, 2:200} | |
| // pos 15: row -> row5[2] = ("Alice", 30) | |
| std::vector<int32_t> row_offsets = {0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 5}; | |
| std::vector<int32_t> field_offsets = {0, 1, 2, 3, 4, 0, 1, 2, 0, 1, 0, 1, 0, 0, 1, 2}; | |
| DataEvolutionRow evolution({row0, row1, row2, row3, row4, row5}, row_offsets, field_offsets); | |
| ASSERT_EQ(evolution.GetFieldCount(), 16); | |
| // Boolean | |
| ASSERT_FALSE(evolution.IsNullAt(0)); | |
| ASSERT_EQ(evolution.GetBoolean(0), true); | |
| // Byte | |
| ASSERT_EQ(evolution.GetByte(1), 42); | |
| // Short | |
| ASSERT_EQ(evolution.GetShort(2), 1024); | |
| // Int | |
| ASSERT_EQ(evolution.GetInt(3), 100000); | |
| // Date | |
| ASSERT_EQ(evolution.GetDate(4), 19000); | |
| // Long | |
| ASSERT_EQ(evolution.GetLong(5), -987654321LL); | |
| // Float | |
| ASSERT_FLOAT_EQ(evolution.GetFloat(6), 3.14f); | |
| // Double | |
| ASSERT_DOUBLE_EQ(evolution.GetDouble(7), -9.87654321); | |
| // String | |
| ASSERT_EQ(evolution.GetString(8).ToString(), "hello"); | |
| // StringView | |
| ASSERT_EQ(evolution.GetStringView(9), "world"); | |
| // Decimal | |
| ASSERT_EQ(evolution.GetDecimal(10, 10, 2), Decimal(10, 2, 67890)); | |
| // Timestamp | |
| ASSERT_EQ(evolution.GetTimestamp(11, 9), Timestamp(1000, 500)); | |
| // Binary | |
| auto retrieved_binary = evolution.GetBinary(12); | |
| Bytes expected_binary("fghij", pool.get()); | |
| ASSERT_EQ(*retrieved_binary, expected_binary); | |
| // Nested Array | |
| auto retrieved_array = evolution.GetArray(13); | |
| ASSERT_OK_AND_ASSIGN(auto inner_values, retrieved_array->ToIntArray()); | |
| ASSERT_EQ(inner_values, std::vector<int32_t>({10, 20})); | |
| // Map | |
| auto retrieved_map = evolution.GetMap(14); | |
| ASSERT_EQ(retrieved_map->Size(), 2); | |
| // Nested Row | |
| auto retrieved_row = evolution.GetRow(15, 2); | |
| ASSERT_EQ(retrieved_row->GetString(0).ToString(), "Alice"); | |
| ASSERT_EQ(retrieved_row->GetInt(1), 30); | |
| // ToString | |
| ASSERT_EQ(evolution.ToString(), "DataEvolutionRow"); | |
| } | |
| } // namespace paimon::test |