blob: 5307707121d619856418d9542b2df0f90a88f475 [file] [log] [blame]
/*
* Copyright 2024-present Alibaba Inc.
*
* Licensed 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/data/decimal.h"
#include <memory>
#include <utility>
#include "gtest/gtest.h"
#include "paimon/common/utils/decimal_utils.h"
#include "paimon/fs/file_system.h"
#include "paimon/fs/local/local_file_system.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/result.h"
#include "paimon/status.h"
#include "paimon/testing/utils/testharness.h"
namespace paimon::test {
TEST(DecimalTest, TestSimple) {
auto CheckResult = [](const Decimal& decimal, const std::vector<uint8_t>& bytes) {
auto pool = GetDefaultPool();
// prepare java bytes
auto java_bytes = Bytes::AllocateBytes(bytes.size(), pool.get());
memcpy(java_bytes->data(), bytes.data(), bytes.size());
// prepare result cpp bytes
auto cpp_serialized_bytes = decimal.ToUnscaledBytes();
// check serialized bytes equal
ASSERT_EQ(std::vector<char>(java_bytes->data(), java_bytes->data() + java_bytes->size()),
cpp_serialized_bytes);
// check deserialize equal
auto decimal2 = Decimal::FromUnscaledBytes(38, 38, java_bytes.get());
ASSERT_EQ(decimal, decimal2);
};
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("12345678998765432145678").value());
std::vector<uint8_t> java_bytes = {0x2, 0x9d, 0x42, 0xb6, 0xa7, 0x2a, 0x9d, 0xc7, 0x7, 0xe};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("-12345678998765432145678").value());
std::vector<uint8_t> java_bytes = {0xfd, 0x62, 0xbd, 0x49, 0x58,
0xd5, 0x62, 0x38, 0xf8, 0xf2};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("0").value());
std::vector<uint8_t> java_bytes = {0x0};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("1").value());
std::vector<uint8_t> java_bytes = {0x1};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("-1").value());
std::vector<uint8_t> java_bytes = {0xff};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, DecimalUtils::StrToInt128("128").value());
std::vector<uint8_t> java_bytes = {0x0, 0x80};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, Decimal::INT128_MINIMUM_VALUE);
std::vector<uint8_t> java_bytes = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
CheckResult(decimal, java_bytes);
}
{
Decimal decimal(38, 38, Decimal::INT128_MAXIMUM_VALUE);
std::vector<uint8_t> java_bytes = {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
CheckResult(decimal, java_bytes);
}
}
TEST(DecimalTest, TestCompatibleWithJava) {
auto pool = GetDefaultPool();
auto file_system = std::make_unique<LocalFileSystem>();
auto file_name = paimon::test::GetDataDir() + "/decimal_bytes.data";
uint64_t file_length = file_system->GetFileStatus(file_name).value()->GetLen();
ASSERT_GT(file_length, 0);
ASSERT_OK_AND_ASSIGN(auto input_stream, file_system->Open(file_name));
auto data_bytes = Bytes::AllocateBytes(file_length, pool.get());
ASSERT_OK(input_stream->Read(data_bytes->data(), file_length));
auto byte_array_input_stream =
std::make_shared<ByteArrayInputStream>(data_bytes->data(), file_length);
DataInputStream data_input_stream(byte_array_input_stream);
for (int32_t i = 0; i < 2000; i++) {
// read decimal str
ASSERT_OK_AND_ASSIGN(int32_t decimal_str_len, data_input_stream.ReadValue<int32_t>());
auto decimal_str = Bytes::AllocateBytes(decimal_str_len, pool.get());
ASSERT_OK(data_input_stream.ReadBytes(decimal_str.get()));
// read decimal serialized bytes from java
ASSERT_OK_AND_ASSIGN(int32_t decimal_bytes_len, data_input_stream.ReadValue<int32_t>());
auto decimal_bytes = Bytes::AllocateBytes(decimal_bytes_len, pool.get());
ASSERT_OK(data_input_stream.ReadBytes(decimal_bytes.get()));
// check result
auto str = std::string(decimal_str->data(), decimal_str->size());
Decimal decimal(/*precision=*/29, /*scale=*/29, DecimalUtils::StrToInt128(str).value());
auto serialized_bytes = decimal.ToUnscaledBytes();
ASSERT_EQ(
std::vector<char>(decimal_bytes->data(), decimal_bytes->data() + decimal_bytes->size()),
serialized_bytes);
auto decimal2 =
Decimal::FromUnscaledBytes(/*precision=*/29, /*scale=*/29, decimal_bytes.get());
ASSERT_EQ(decimal, decimal2);
}
}
TEST(DecimalTest, TestCompareTo) {
auto CheckResult = [](const Decimal& decimal1, const Decimal& decimal2) {
ASSERT_FALSE(decimal1 < decimal1);
ASSERT_FALSE(decimal1 > decimal1);
ASSERT_EQ(decimal1, decimal1);
ASSERT_EQ(decimal1.CompareTo(decimal2), -1);
ASSERT_LT(decimal1, decimal2);
ASSERT_EQ(decimal2.CompareTo(decimal1), 1);
ASSERT_GT(decimal2, decimal1);
auto decimal3 = decimal1;
ASSERT_EQ(decimal3.CompareTo(decimal1), 0);
ASSERT_EQ(decimal3, decimal1);
Decimal negative_decimal1(decimal1.Precision(), decimal1.Scale(), -decimal1.Value());
Decimal negative_decimal2(decimal2.Precision(), decimal2.Scale(), -decimal2.Value());
ASSERT_EQ(negative_decimal1.CompareTo(negative_decimal2), 1);
ASSERT_GT(negative_decimal1, negative_decimal2);
ASSERT_EQ(negative_decimal2.CompareTo(negative_decimal1), -1);
ASSERT_LT(negative_decimal2, negative_decimal1);
auto negative_decimal3 = negative_decimal1;
ASSERT_EQ(negative_decimal3.CompareTo(negative_decimal1), 0);
ASSERT_EQ(negative_decimal3, negative_decimal1);
};
auto CheckEqual = [](const Decimal& decimal1, const Decimal& decimal2) {
ASSERT_EQ(decimal1.CompareTo(decimal2), 0);
ASSERT_EQ(decimal2.CompareTo(decimal1), 0);
Decimal negative_decimal1(decimal1.Precision(), decimal1.Scale(), -decimal1.Value());
Decimal negative_decimal2(decimal2.Precision(), decimal2.Scale(), -decimal2.Value());
ASSERT_EQ(negative_decimal1.CompareTo(negative_decimal2), 0);
ASSERT_EQ(negative_decimal2.CompareTo(negative_decimal1), 0);
};
// same scales
{
Decimal decimal1(23, 0, DecimalUtils::StrToInt128("99").value());
Decimal decimal2(23, 0, DecimalUtils::StrToInt128("100").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 5, DecimalUtils::StrToInt128("34543").value());
Decimal decimal2(23, 5, DecimalUtils::StrToInt128("4324324").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 15, DecimalUtils::StrToInt128("345345435432").value());
Decimal decimal2(23, 15, DecimalUtils::StrToInt128("345344425435432").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 20, DecimalUtils::StrToInt128("5").value());
Decimal decimal2(23, 20, DecimalUtils::StrToInt128("50").value());
CheckResult(decimal1, decimal2);
}
// different scales
{
Decimal decimal1(23, 4, DecimalUtils::StrToInt128("10000").value());
Decimal decimal2(23, 3, DecimalUtils::StrToInt128("10000").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 2, DecimalUtils::StrToInt128("111").value());
Decimal decimal2(23, 3, DecimalUtils::StrToInt128("1111").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 5, DecimalUtils::StrToInt128("999999").value());
Decimal decimal2(23, 5, DecimalUtils::StrToInt128("9999999").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 1, DecimalUtils::StrToInt128("100").value());
Decimal decimal2(23, 0, DecimalUtils::StrToInt128("99").value());
CheckResult(decimal1, decimal2);
}
// same integral parts
{
Decimal decimal1(23, 0, DecimalUtils::StrToInt128("99999").value());
Decimal decimal2(23, 1, DecimalUtils::StrToInt128("999999").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(23, 3, DecimalUtils::StrToInt128("12345123").value());
Decimal decimal2(23, 5, DecimalUtils::StrToInt128("1234553432").value());
CheckResult(decimal1, decimal2);
}
// equal numbers
{
Decimal decimal1(23, 3, DecimalUtils::StrToInt128("100000").value());
Decimal decimal2(23, 0, DecimalUtils::StrToInt128("100").value());
CheckEqual(decimal1, decimal2);
}
{
Decimal decimal1(23, 3, DecimalUtils::StrToInt128("100000").value());
Decimal decimal2(23, 3, DecimalUtils::StrToInt128("100000").value());
CheckEqual(decimal1, decimal2);
}
{
Decimal decimal1(23, 10, DecimalUtils::StrToInt128("1").value());
Decimal decimal2(23, 11, DecimalUtils::StrToInt128("10").value());
CheckEqual(decimal1, decimal2);
}
// large scales (>18)
{
Decimal decimal1(128, 35, DecimalUtils::StrToInt128("99").value());
Decimal decimal2(128, 35, DecimalUtils::StrToInt128("100").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(
128, 29, DecimalUtils::StrToInt128("12345678999999999999999999999999999998").value());
Decimal decimal2(
128, 30, DecimalUtils::StrToInt128("123456789999999999999999999999999999999").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(
128, 30, DecimalUtils::StrToInt128("123456789999999999999999999999999999900").value());
Decimal decimal2(
128, 29, DecimalUtils::StrToInt128("12345678999999999999999999999999999990").value());
CheckEqual(decimal1, decimal2);
}
// minimum and maximum
{
Decimal decimal1(128, 39, Decimal::INT128_MAXIMUM_VALUE);
Decimal decimal2(
128, 39, DecimalUtils::StrToInt128("170141183460469231731687303715884105727").value());
CheckEqual(decimal1, decimal2);
}
{
Decimal decimal1(128, 39, Decimal::INT128_MINIMUM_VALUE);
Decimal decimal2(
128, 39, DecimalUtils::StrToInt128("-170141183460469231731687303715884105728").value());
CheckEqual(decimal1, decimal2);
}
// fractional overflow
{
Decimal decimal1(128, 39, Decimal::INT128_MAXIMUM_VALUE);
Decimal decimal2(
128, 38, DecimalUtils::StrToInt128("99999999999999999999999999999999999999").value());
CheckResult(decimal1, decimal2);
}
{
Decimal decimal1(
128, 38, DecimalUtils::StrToInt128("-99999999999999999999999999999999999999").value());
Decimal decimal2(128, 39, Decimal::INT128_MINIMUM_VALUE);
CheckResult(decimal1, decimal2);
}
}
} // namespace paimon::test