| /* | |
| * 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/data/binary_row.h" | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <ctime> | |
| #include <optional> | |
| #include <set> | |
| #include "gtest/gtest.h" | |
| #include "paimon/common/data/binary_row_writer.h" | |
| #include "paimon/common/data/serializer/binary_row_serializer.h" | |
| #include "paimon/common/io/memory_segment_output_stream.h" | |
| #include "paimon/common/memory/memory_segment_utils.h" | |
| #include "paimon/common/utils/date_time_utils.h" | |
| #include "paimon/common/utils/decimal_utils.h" | |
| #include "paimon/common/utils/serialization_utils.h" | |
| #include "paimon/io/byte_array_input_stream.h" | |
| #include "paimon/io/data_input_stream.h" | |
| #include "paimon/memory/bytes.h" | |
| #include "paimon/memory/memory_pool.h" | |
| #include "paimon/memory/memory_segment.h" | |
| #include "paimon/status.h" | |
| #include "paimon/testing/utils/testharness.h" | |
| namespace paimon::test { | |
| class BinaryRowTest : public testing::Test { | |
| private: | |
| void AssertTestWriterRow(const BinaryRow& row) { | |
| ASSERT_EQ(row.GetString(0).ToString(), "1"); | |
| ASSERT_EQ(row.GetInt(8), 88); | |
| ASSERT_EQ(row.GetShort(11), static_cast<int16_t>(292)); | |
| ASSERT_EQ(row.GetLong(10), 284); | |
| ASSERT_EQ(row.GetByte(2), static_cast<char>(99)); | |
| ASSERT_EQ(row.GetDouble(6), static_cast<double>(87.1)); | |
| ASSERT_EQ(row.GetFloat(7), 26.1f); | |
| ASSERT_TRUE(row.GetBoolean(1)); | |
| ASSERT_EQ(row.GetString(3).ToString(), "1234567"); | |
| ASSERT_EQ(row.GetString(5).ToString(), "12345678"); | |
| ASSERT_EQ(row.GetString(9).ToString(), "啦啦啦啦啦我是快乐的粉刷匠"); | |
| ASSERT_EQ(row.GetString(9).HashCode(), | |
| BinaryString::FromString("啦啦啦啦啦我是快乐的粉刷匠", GetDefaultPool().get()) | |
| .HashCode()); | |
| ASSERT_TRUE(row.IsNullAt(12)); | |
| } | |
| }; | |
| TEST_F(BinaryRowTest, TestBasic) { | |
| // consider header 1 byte. | |
| ASSERT_EQ(BinaryRow(0).GetFixedLengthPartSize(), 8); | |
| ASSERT_EQ(BinaryRow(1).GetFixedLengthPartSize(), 16); | |
| ASSERT_EQ(BinaryRow(65).GetFixedLengthPartSize(), 536); | |
| ASSERT_EQ(BinaryRow(128).GetFixedLengthPartSize(), 1048); | |
| auto pool = GetDefaultPool(); | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(100, pool.get()); | |
| MemorySegment segment = MemorySegment::Wrap(bytes); | |
| BinaryRow row(2); | |
| row.PointTo(segment, 10, 48); | |
| row.SetInt(0, 5); | |
| row.SetDouble(1, 5.8); | |
| ASSERT_EQ(5, row.GetInt(0)); | |
| ASSERT_EQ(static_cast<double>(5.8), row.GetDouble(1)); | |
| row.Clear(); | |
| ASSERT_EQ(0, row.HashCode()); | |
| std::shared_ptr<Bytes> bytes1 = Bytes::AllocateBytes(100, pool.get()); | |
| MemorySegment segment1 = MemorySegment::Wrap(bytes1); | |
| row.PointTo(segment1, 0, 20); | |
| row.SetInt(0, 5); | |
| ASSERT_EQ(5, row.GetInt(0)); | |
| } | |
| TEST_F(BinaryRowTest, TestSetAndGet) { | |
| auto pool = GetDefaultPool(); | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(100, pool.get()); | |
| MemorySegment segment = MemorySegment::Wrap(bytes); | |
| BinaryRow row(9); | |
| row.PointTo(segment, 20, 80); | |
| row.SetNullAt(0); | |
| row.SetInt(1, 11); | |
| row.SetLong(2, 22); | |
| row.SetDouble(3, 33); | |
| row.SetBoolean(4, true); | |
| row.SetShort(5, static_cast<int16_t>(55)); | |
| row.SetByte(6, static_cast<char>(66)); | |
| row.SetFloat(7, static_cast<float>(77)); | |
| ASSERT_EQ(row.GetInt(1), 11); | |
| ASSERT_TRUE(row.IsNullAt(0)); | |
| ASSERT_EQ(row.GetShort(5), static_cast<int16_t>(55)); | |
| ASSERT_EQ(row.GetLong(2), 22L); | |
| ASSERT_TRUE(row.GetBoolean(4)); | |
| ASSERT_EQ(row.GetByte(6), static_cast<char>(66)); | |
| ASSERT_EQ(row.GetFloat(7), static_cast<float>(77)); | |
| ASSERT_EQ(row.GetDouble(3), static_cast<double>(33)); | |
| } | |
| TEST_F(BinaryRowTest, TestHeaderSize) { | |
| ASSERT_EQ(BinaryRow::CalculateBitSetWidthInBytes(56), 8); | |
| ASSERT_EQ(BinaryRow::CalculateBitSetWidthInBytes(57), 16); | |
| ASSERT_EQ(BinaryRow::CalculateBitSetWidthInBytes(120), 16); | |
| ASSERT_EQ(BinaryRow::CalculateBitSetWidthInBytes(121), 24); | |
| } | |
| TEST_F(BinaryRowTest, TestWriter) { | |
| auto pool = GetDefaultPool(); | |
| int32_t arity = 13; | |
| BinaryRow row(arity); | |
| BinaryRowWriter writer(&row, 20, pool.get()); | |
| writer.WriteString(0, BinaryString::FromString("1", pool.get())); | |
| writer.WriteString(3, BinaryString::FromString("1234567", pool.get())); | |
| writer.WriteString(5, BinaryString::FromString("12345678", pool.get())); | |
| writer.WriteString(9, BinaryString::FromString("啦啦啦啦啦我是快乐的粉刷匠", pool.get())); | |
| writer.WriteBoolean(1, true); | |
| writer.WriteByte(2, static_cast<char>(99)); | |
| writer.WriteDouble(6, 87.1); | |
| writer.WriteFloat(7, 26.1f); | |
| writer.WriteInt(8, 88); | |
| writer.WriteLong(10, 284); | |
| writer.WriteShort(11, static_cast<int16_t>(292)); | |
| writer.SetNullAt(12); | |
| writer.Complete(); | |
| AssertTestWriterRow(row); | |
| // test copy | |
| auto copied1 = row.Copy(pool.get()); | |
| AssertTestWriterRow(copied1); | |
| ASSERT_EQ(row, copied1); | |
| // test copy | |
| BinaryRow copied2(arity); | |
| row.Copy(&copied2, pool.get()); | |
| AssertTestWriterRow(copied2); | |
| ASSERT_EQ(row, copied2); | |
| } | |
| TEST_F(BinaryRowTest, TestWriteString) { | |
| auto pool = GetDefaultPool(); | |
| { | |
| // little byte[] | |
| BinaryRow row(1); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| char chars[2]; | |
| chars[0] = static_cast<char>(0xFFFF); | |
| chars[1] = 0; | |
| writer.WriteString(0, BinaryString::FromString(std::string(chars, 2), pool.get())); | |
| writer.Complete(); | |
| std::string str = row.GetString(0).ToString(); | |
| ASSERT_EQ(2, str.length()); | |
| ASSERT_EQ(str[0], chars[0]); | |
| ASSERT_EQ(str[1], chars[1]); | |
| } | |
| { | |
| // big byte[] | |
| std::string str = "啦啦啦啦啦我是快乐的粉刷匠"; | |
| BinaryRow row(2); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteString(0, BinaryString::FromString(str, pool.get())); | |
| writer.WriteString(1, BinaryString::FromBytes(Bytes::AllocateBytes(str, pool.get()))); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), str); | |
| ASSERT_EQ(row.GetString(1).ToString(), str); | |
| } | |
| } | |
| TEST_F(BinaryRowTest, TestWriteBytes) { | |
| auto pool = GetDefaultPool(); | |
| { | |
| BinaryRow row(1); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| std::string str = "啦啦啦啦啦我是快乐的粉刷匠"; | |
| Bytes bytes(str, pool.get()); | |
| writer.WriteBytes(0, bytes); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), str); | |
| } | |
| { | |
| BinaryRow row(1); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| std::string str = "啦"; | |
| Bytes bytes(str, pool.get()); | |
| writer.WriteBytes(0, bytes); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), str); | |
| } | |
| } | |
| TEST_F(BinaryRowTest, TestReuseWriter) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(2); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteString(0, BinaryString::FromString("01234567", pool.get())); | |
| writer.WriteString(1, BinaryString::FromString("012345678", pool.get())); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), "01234567"); | |
| ASSERT_EQ(row.GetString(1).ToString(), "012345678"); | |
| writer.Reset(); | |
| writer.WriteString(0, BinaryString::FromString("1", pool.get())); | |
| writer.WriteString(1, BinaryString::FromString("0123456789", pool.get())); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), "1"); | |
| ASSERT_EQ(row.GetString(1).ToString(), "0123456789"); | |
| } | |
| TEST_F(BinaryRowTest, TestAnyNull) { | |
| auto pool = GetDefaultPool(); | |
| { | |
| BinaryRow row(3); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| ASSERT_FALSE(row.AnyNull()); | |
| // test header should not compute by anyNull | |
| row.SetRowKind(RowKind::UpdateBefore()); | |
| ASSERT_FALSE(row.AnyNull()); | |
| writer.SetNullAt(2); | |
| ASSERT_TRUE(row.AnyNull()); | |
| writer.SetNullAt(0); | |
| ASSERT_TRUE(row.AnyNull({0, 1, 2})); | |
| ASSERT_FALSE(row.AnyNull({1})); | |
| writer.SetNullAt(1); | |
| ASSERT_TRUE(row.AnyNull()); | |
| } | |
| int32_t num_fields = 80; | |
| for (int32_t i = 0; i < num_fields; i++) { | |
| BinaryRow row(num_fields); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| row.SetRowKind(RowKind::Delete()); | |
| ASSERT_FALSE(row.AnyNull()); | |
| writer.SetNullAt(i); | |
| ASSERT_TRUE(row.AnyNull()); | |
| } | |
| } | |
| TEST_F(BinaryRowTest, TestSingleSegmentBinaryRowHashCode) { | |
| auto pool = GetDefaultPool(); | |
| int64_t seed = DateTimeUtils::GetCurrentUTCTimeUs(); | |
| std::srand(seed); | |
| // test hash stabilization | |
| BinaryRow row(13); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| for (int32_t i = 0; i < 99; i++) { | |
| writer.Reset(); | |
| writer.WriteString( | |
| 0, BinaryString::FromString("" + std::to_string(static_cast<int32_t>(std::rand())), | |
| pool.get())); | |
| writer.WriteString(3, BinaryString::FromString("01234567", pool.get())); | |
| writer.WriteString(5, BinaryString::FromString("012345678", pool.get())); | |
| writer.WriteString(9, BinaryString::FromString("啦啦啦啦啦我是快乐的粉刷匠", pool.get())); | |
| writer.WriteBoolean(1, true); | |
| writer.WriteByte(2, static_cast<char>(99)); | |
| writer.WriteDouble(6, 87.1); | |
| writer.WriteFloat(7, 26.1f); | |
| writer.WriteInt(8, 88); | |
| writer.WriteLong(10, 284); | |
| writer.WriteShort(11, static_cast<int16_t>(292)); | |
| writer.SetNullAt(12); | |
| writer.Complete(); | |
| BinaryRow copy = row.Copy(pool.get()); | |
| ASSERT_EQ(copy.HashCode(), row.HashCode()) << "seed: " << seed << ", idx: " << i; | |
| } | |
| // test hash distribution | |
| int32_t count = 999999; | |
| std::set<int32_t> hash_codes; | |
| for (int32_t i = 0; i < count; i++) { | |
| row.SetInt(8, i); | |
| hash_codes.insert(row.HashCode()); | |
| } | |
| ASSERT_EQ(hash_codes.size(), count); | |
| hash_codes.clear(); | |
| BinaryRow row2(1); | |
| BinaryRowWriter writer2(&row2, 0, pool.get()); | |
| for (int32_t i = 0; i < count; i++) { | |
| writer2.Reset(); | |
| writer2.WriteString(0, BinaryString::FromString( | |
| "啦啦啦啦啦我是快乐的粉刷匠" + std::to_string(i), pool.get())); | |
| writer2.Complete(); | |
| hash_codes.insert(row2.HashCode()); | |
| } | |
| ASSERT_GT(hash_codes.size(), static_cast<size_t>(count * 0.997)); | |
| } | |
| TEST_F(BinaryRowTest, TestHeader) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(2); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteInt(0, 10); | |
| writer.SetNullAt(1); | |
| writer.WriteRowKind(RowKind::UpdateBefore()); | |
| writer.Complete(); | |
| BinaryRow new_row = row.Copy(pool.get()); | |
| ASSERT_EQ(new_row, row); | |
| ASSERT_EQ(new_row.GetRowKind().value(), RowKind::UpdateBefore()); | |
| new_row.SetRowKind(RowKind::Delete()); | |
| ASSERT_EQ(new_row.GetRowKind().value(), RowKind::Delete()); | |
| } | |
| TEST_F(BinaryRowTest, TestDefaultRowKind) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(1); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteInt(0, 10); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetRowKind().value(), RowKind::Insert()); | |
| } | |
| TEST_F(BinaryRowTest, TestBinary) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(2); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| // explicit cast to avoid -Wnarrowing on platforms where char is unsigned (e.g. aarch64) | |
| char chars1[3] = {1, static_cast<char>(-1), 5}; | |
| char chars2[8] = {1, static_cast<char>(-1), 5, 5, 1, 5, 1, 5}; | |
| std::string str1(chars1, 3); | |
| std::string str2(chars2, 8); | |
| Bytes bytes1(str1, pool.get()); | |
| Bytes bytes2(str2, pool.get()); | |
| writer.WriteBinary(0, bytes1); | |
| writer.WriteBinary(1, bytes2); | |
| writer.Complete(); | |
| ASSERT_EQ(*row.GetBinary(0), bytes1); | |
| ASSERT_EQ(*row.GetBinary(1), bytes2); | |
| } | |
| TEST_F(BinaryRowTest, TestCompatibleWithJava) { | |
| auto pool = GetDefaultPool(); | |
| { | |
| int32_t arity = 1; | |
| BinaryRow row(arity); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteString(0, BinaryString::FromString("Alice2", pool.get())); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetString(0).ToString(), "Alice2"); | |
| auto bytes = SerializationUtils::SerializeBinaryRow(row, pool.get()); | |
| std::string bytes_view(bytes->data(), bytes->size()); | |
| std::vector<uint8_t> expect_bytes = {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 65, 108, 105, 99, 101, 50, 0, 134}; | |
| std::string expect_view(reinterpret_cast<char*>(expect_bytes.data()), expect_bytes.size()); | |
| ASSERT_EQ(bytes_view, expect_view); | |
| ASSERT_OK_AND_ASSIGN(auto de_row, SerializationUtils::DeserializeBinaryRow(bytes)); | |
| ASSERT_EQ(1, de_row.GetFieldCount()); | |
| ASSERT_EQ(de_row.GetString(0).ToString(), "Alice2"); | |
| } | |
| { | |
| int32_t arity = 1; | |
| BinaryRow row(arity); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.WriteInt(0, 18); | |
| writer.Complete(); | |
| ASSERT_EQ(row.GetInt(0), 18); | |
| auto bytes = SerializationUtils::SerializeBinaryRow(row, pool.get()); | |
| std::string bytes_view(bytes->data(), bytes->size()); | |
| std::vector<uint8_t> expect_bytes = {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 18, 0, 0, 0, 0, 0, 0, 0}; | |
| std::string expect_view(reinterpret_cast<char*>(expect_bytes.data()), expect_bytes.size()); | |
| ASSERT_EQ(bytes_view, expect_view); | |
| ASSERT_OK_AND_ASSIGN(auto de_row, SerializationUtils::DeserializeBinaryRow(bytes)); | |
| ASSERT_EQ(1, de_row.GetFieldCount()); | |
| ASSERT_EQ(de_row.GetInt(0), 18); | |
| } | |
| } | |
| TEST_F(BinaryRowTest, TestZeroOutPaddingString) { | |
| srand(static_cast<unsigned>(time(nullptr))); | |
| auto pool = GetDefaultPool(); | |
| int32_t bytes_size = 1024; | |
| std::shared_ptr<Bytes> bytes = Bytes::AllocateBytes(1024, pool.get()); | |
| BinaryRow row(1); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.Reset(); | |
| for (int32_t i = 0; i < bytes_size; i++) { | |
| (*bytes)[i] = paimon::test::RandomNumber(0, 255); | |
| } | |
| writer.WriteBinary(0, *bytes); | |
| writer.Reset(); | |
| writer.WriteString(0, BinaryString::FromString("wahahah", pool.get())); | |
| writer.Complete(); | |
| int32_t hash1 = row.HashCode(); | |
| writer.Reset(); | |
| for (int32_t i = 0; i < bytes_size; i++) { | |
| (*bytes)[i] = paimon::test::RandomNumber(0, 255); | |
| } | |
| writer.WriteBinary(0, *bytes); | |
| writer.Reset(); | |
| writer.WriteString(0, BinaryString::FromString("wahahah", pool.get())); | |
| writer.Complete(); | |
| int32_t hash2 = row.HashCode(); | |
| ASSERT_EQ(hash2, hash1); | |
| } | |
| TEST_F(BinaryRowTest, TestWriteTimestampAndDecimal) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(7); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| // timestamp with millis precision, compact | |
| Timestamp timestamp(1723535714123ll, 0); | |
| writer.WriteTimestamp(0, timestamp, Timestamp::MILLIS_PRECISION); | |
| // timestamp with default precision, not compact | |
| Timestamp timestamp1(1723535713000ll, 1234); | |
| writer.WriteTimestamp(1, timestamp1, Timestamp::DEFAULT_PRECISION); | |
| // 1234.56, compact, precision 6, scale 2 | |
| Decimal decimal(6, 2, 123456); | |
| writer.WriteDecimal(2, decimal, 6); | |
| // 123456789987654321.45678, not compact, precision 23, scale 5 | |
| Decimal decimal3(23, 5, DecimalUtils::StrToInt128("12345678998765432145678").value()); | |
| writer.WriteDecimal(3, decimal3, 23); | |
| // 0, not compact, precision 27, scale 4 | |
| Decimal decimal4(27, 4, 0); | |
| writer.WriteDecimal(4, decimal4, 27); | |
| // -1234.56, compact, precision 6, scale 2 | |
| Decimal decimal5(6, 2, -123456); | |
| writer.WriteDecimal(5, decimal5, 6); | |
| // -123456789987654321.45678, not compact, precision 23, scale 5 | |
| Decimal decimal6(23, 5, DecimalUtils::StrToInt128("-12345678998765432145678").value()); | |
| writer.WriteDecimal(6, decimal6, 23); | |
| writer.Complete(); | |
| // test serialize | |
| std::vector<uint8_t> expected = { | |
| 0, 0, 0, 0, 0, 0, 0, 0, 75, 231, 187, 74, 145, 1, 0, 0, 210, 4, 0, 0, | |
| 64, 0, 0, 0, 64, 226, 1, 0, 0, 0, 0, 0, 10, 0, 0, 0, 72, 0, 0, 0, | |
| 1, 0, 0, 0, 88, 0, 0, 0, 192, 29, 254, 255, 255, 255, 255, 255, 10, 0, 0, 0, | |
| 104, 0, 0, 0, 232, 226, 187, 74, 145, 1, 0, 0, 2, 157, 66, 182, 167, 42, 157, 199, | |
| 7, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 253, 98, 189, 73, 88, 213, 98, 56, 248, 242, 0, 0, 0, 0, 0, 0}; | |
| auto result_bytes = row.ToBytes(pool.get()); | |
| std::vector<uint8_t> result(result_bytes->size(), 0); | |
| memcpy(result.data(), result_bytes->data(), result_bytes->size()); | |
| ASSERT_EQ(expected, result); | |
| // test hash code | |
| ASSERT_EQ(0x773cf266, row.HashCode()); | |
| ASSERT_EQ(timestamp, row.GetTimestamp(0, Timestamp::MILLIS_PRECISION)); | |
| ASSERT_EQ(timestamp1, row.GetTimestamp(1, Timestamp::DEFAULT_PRECISION)); | |
| ASSERT_EQ(decimal, row.GetDecimal(2, 6, 2)); | |
| ASSERT_EQ(decimal3, row.GetDecimal(3, 23, 5)); | |
| ASSERT_EQ(decimal4, row.GetDecimal(4, 27, 4)); | |
| ASSERT_EQ(decimal5, row.GetDecimal(5, 6, 2)); | |
| ASSERT_EQ(decimal6, row.GetDecimal(6, 23, 5)); | |
| } | |
| TEST_F(BinaryRowTest, TestWriteTimestampAndDecimalWithNull) { | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(5); | |
| BinaryRowWriter writer(&row, 0, pool.get()); | |
| writer.Reset(); | |
| // timestamp with max precision, compact | |
| writer.WriteTimestamp(0, std::nullopt, Timestamp::MAX_PRECISION); | |
| // timestamp with MAX_PRECISION, compact | |
| Timestamp timestamp(1723535713567ll, 1234); | |
| writer.WriteTimestamp(1, timestamp, Timestamp::MAX_PRECISION); | |
| // decimal precision 27, not compact | |
| writer.WriteDecimal(2, std::nullopt, 27); | |
| // -123456789987654321.45678, not compact, precision 23, scale 5 | |
| Decimal decimal(23, 5, DecimalUtils::StrToInt128("-12345678998765432145678").value()); | |
| writer.WriteDecimal(3, decimal, 23); | |
| writer.SetNullAt(4); | |
| writer.Complete(); | |
| // test serialize | |
| std::vector<uint8_t> expected = { | |
| 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 210, 4, 0, 0, 56, 0, 0, 0, | |
| 0, 0, 0, 0, 64, 0, 0, 0, 10, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 31, 229, 187, 74, 145, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 253, 98, 189, 73, 88, 213, 98, 56, 248, 242, 0, 0, 0, 0, 0, 0}; | |
| auto result_bytes = row.ToBytes(pool.get()); | |
| std::vector<uint8_t> result(result_bytes->size(), 0); | |
| memcpy(result.data(), result_bytes->data(), result_bytes->size()); | |
| ASSERT_EQ(expected, result); | |
| // test hash code | |
| ASSERT_EQ(0xedbc7d65, row.HashCode()); | |
| // test value in binary row | |
| ASSERT_TRUE(row.IsNullAt(0)); | |
| ASSERT_EQ(timestamp, row.GetTimestamp(1, Timestamp::MAX_PRECISION)); | |
| ASSERT_TRUE(row.IsNullAt(2)); | |
| ASSERT_EQ(decimal, row.GetDecimal(3, 23, 5)); | |
| ASSERT_TRUE(row.IsNullAt(4)); | |
| } | |
| TEST_F(BinaryRowTest, TestBinaryRowSerializer) { | |
| srand(static_cast<unsigned>(time(nullptr))); | |
| auto pool = GetDefaultPool(); | |
| BinaryRow row(3); | |
| BinaryRowWriter writer(&row, /*initial_size=*/1024, pool.get()); | |
| writer.WriteInt(0, paimon::test::RandomNumber(0, 2000000)); | |
| int32_t str_size = 1024; | |
| std::string test_string1, test_string2; | |
| test_string1.reserve(str_size); | |
| test_string2.reserve(str_size); | |
| for (int32_t j = 0; j < str_size; j++) { | |
| test_string1 += static_cast<char>(paimon::test::RandomNumber(0, 25) + 'a'); | |
| test_string2 += static_cast<char>(paimon::test::RandomNumber(0, 25) + 'a'); | |
| } | |
| writer.WriteStringView(1, std::string_view{test_string1}); | |
| writer.WriteStringView(2, std::string_view{test_string2}); | |
| writer.Complete(); | |
| BinaryRowSerializer row_serializer(3, pool); | |
| MemorySegmentOutputStream out(MemorySegmentOutputStream::DEFAULT_SEGMENT_SIZE, pool); | |
| ASSERT_OK(row_serializer.Serialize(row, &out)); | |
| PAIMON_UNIQUE_PTR<Bytes> bytes_serialize = | |
| MemorySegmentUtils::CopyToBytes(out.Segments(), 0, out.CurrentSize(), pool.get()); | |
| ASSERT_GE(bytes_serialize->size(), sizeof(int32_t) + test_string1.size() + test_string2.size()); | |
| DataInputStream input( | |
| std::make_shared<ByteArrayInputStream>(bytes_serialize->data(), bytes_serialize->size())); | |
| ASSERT_OK_AND_ASSIGN(auto de_row, row_serializer.Deserialize(&input)); | |
| ASSERT_EQ(de_row, row); | |
| ASSERT_EQ(de_row.GetString(1).ToString(), test_string1); | |
| ASSERT_EQ(de_row.GetString(2).ToString(), test_string2); | |
| } | |
| } // namespace paimon::test |