blob: 87d3652827830e49f19197a4682a22fb1f94c347 [file] [log] [blame]
// 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 <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <limits>
#include <sstream>
#include <boost/cstdint.hpp>
#include <boost/lexical_cast.hpp>
#include "runtime/decimal-value.inline.h"
#include "runtime/raw-value.h"
#include "runtime/types.h"
#include "testutil/gtest-util.h"
#include "util/string-parser.h"
#include "common/names.h"
using std::max;
using std::min;
namespace impala {
// Compare decimal result against double.
static const double MAX_ERROR = 0.00005;
template <typename T>
void VerifyEquals(const DecimalValue<T>& t1, const DecimalValue<T>& t2) {
if (t1 != t2) {
LOG(ERROR) << t1 << " != " << t2;
EXPECT_TRUE(false);
}
}
template <typename T>
void VerifyParse(const string& s, int precision, int scale, bool round,
const DecimalValue<T>& expected_val, StringParser::ParseResult expected_result) {
StringParser::ParseResult parse_result;
DecimalValue<T> val = StringParser::StringToDecimal<T>(
s.c_str(), s.size(), precision, scale, round, &parse_result);
EXPECT_EQ(expected_result, parse_result) << "Failed test string: " << s;
if (expected_result == StringParser::PARSE_SUCCESS ||
expected_result == StringParser::PARSE_UNDERFLOW) {
VerifyEquals(expected_val, val);
}
}
template <typename T>
void VerifyParse(const string& s, int precision, int scale,
const DecimalValue<T>& expected_val, StringParser::ParseResult expected_result) {
VerifyParse(s, precision, scale, false, expected_val, expected_result);
VerifyParse(s, precision, scale, true, expected_val, expected_result);
}
template <typename T>
void VerifyParse(const string& s, int precision, int scale,
const DecimalValue<T>& expected_val_v1, StringParser::ParseResult expected_result_v1,
const DecimalValue<T>& expected_val_v2, StringParser::ParseResult expected_result_v2)
{
VerifyParse(s, precision, scale, false, expected_val_v1, expected_result_v1);
VerifyParse(s, precision, scale, true, expected_val_v2, expected_result_v2);
}
template<typename T>
void VerifyToString(const T& decimal, int precision, int scale, const string& expected) {
EXPECT_EQ(decimal.ToString(precision, scale), expected);
}
void StringToAllDecimals(const string& s, int precision, int scale, int32_t val,
StringParser::ParseResult result) {
VerifyParse(s, precision, scale, Decimal4Value(val), result);
VerifyParse(s, precision, scale, Decimal8Value(val), result);
VerifyParse(s, precision, scale, Decimal16Value(val), result);
}
void StringToAllDecimals(const string& s, int precision, int scale,
int32_t val_v1, StringParser::ParseResult result_v1,
int32_t val_v2, StringParser::ParseResult result_v2) {
VerifyParse(s, precision, scale,
Decimal4Value(val_v1), result_v1, Decimal4Value(val_v2), result_v2);
VerifyParse(s, precision, scale,
Decimal8Value(val_v1), result_v1, Decimal8Value(val_v2), result_v2);
VerifyParse(s, precision, scale,
Decimal16Value(val_v1), result_v1, Decimal16Value(val_v2), result_v2);
}
TEST(DecimalTest, IntToDecimal) {
Decimal16Value d16;
bool overflow = false;
d16 = Decimal16Value::FromInt(27, 18, -25559, &overflow);
EXPECT_FALSE(overflow);
VerifyToString(d16, 27, 18, "-25559.000000000000000000");
d16 = Decimal16Value::FromInt(36, 29, 32130, &overflow);
EXPECT_FALSE(overflow);
VerifyToString(d16, 36, 29, "32130.00000000000000000000000000000");
d16 = Decimal16Value::FromInt(38, 38, 1, &overflow);
EXPECT_TRUE(overflow);
// Smaller decimal types can't overflow here since the FE should never generate
// that.
}
TEST(DecimalTest, DoubleToDecimal) {
Decimal4Value d4;
Decimal8Value d8;
Decimal16Value d16;
bool overflow = false;
d4 = Decimal4Value::FromDouble(9, 0, 1.9, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 2);
VerifyToString(d4, 9, 0, "2");
d4 = Decimal4Value::FromDouble(9, 0, 1.9, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 1);
VerifyToString(d4, 9, 0, "1");
d4 = Decimal4Value::FromDouble(1, 0, 1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 1);
VerifyToString(d4, 1, 0, "1");
d4 = Decimal4Value::FromDouble(1, 0, 0, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 0);
VerifyToString(d4, 1, 0, "0");
d4 = Decimal4Value::FromDouble(1, 0, 9.9, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 9);
VerifyToString(d4, 1, 0, "9");
d4 = Decimal4Value::FromDouble(1, 0, 9.9, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
d4 = Decimal4Value::FromDouble(1, 0, -1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), -1);
VerifyToString(d4, 1, 0, "-1");
d4 = Decimal4Value::FromDouble(1, 0, -9.9, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), -9);
VerifyToString(d4, 1, 0, "-9");
d4 = Decimal4Value::FromDouble(1, 0, -9.9, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
d4 = Decimal4Value::FromDouble(1, 1, 0.1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 1);
VerifyToString(d4, 1, 1, "0.1");
d4 = Decimal4Value::FromDouble(1, 1, 0.0, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 0);
VerifyToString(d4, 1, 1, "0.0");
d4 = Decimal4Value::FromDouble(1, 1, -0.1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), -1);
VerifyToString(d4, 1, 1, "-0.1");
overflow = false;
d8 = Decimal8Value::FromDouble(10, 5, -100.1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d8.value(), -10010000);
VerifyToString(d8, 10, 5, "-100.10000");
overflow = false;
d16 = Decimal16Value::FromDouble(10, 10, -0.1, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d16.value(), -1000000000);
VerifyToString(d16, 10, 10, "-0.1000000000");
// Test overflow
overflow = false;
Decimal4Value::FromDouble(9, 0, 999999999.123, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
Decimal4Value::FromDouble(9, 0, 1234567890.1, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal8Value::FromDouble(9, 0, -1234567890.123, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal8Value::FromDouble(10, 5, 99999.1234567, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
Decimal8Value::FromDouble(10, 5, 100000.1, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal8Value::FromDouble(10, 5, -123456.123, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
d16 = Decimal16Value::FromDouble(10, 10, 0.1234, true, &overflow);
EXPECT_FALSE(overflow);
VerifyToString(d16, 10, 10, "0.1234000000");
overflow = false;
Decimal16Value::FromDouble(10, 10, 1.1, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal16Value::FromDouble(10, 10, -1.1, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal16Value::FromDouble(10, 10, 0.99999999999, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
Decimal16Value::FromDouble(10, 10, 0.99999999999, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
Decimal16Value::FromDouble(10, 10, -0.99999999999, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
Decimal16Value::FromDouble(10, 10, -0.99999999999, true, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
// Test half rounding behavior
d4 = Decimal4Value::FromDouble(1, 0, 0.499999999, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 0);
VerifyToString(d4, 1, 0, "0");
d4 = Decimal4Value::FromDouble(1, 0, 0.5, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 1);
VerifyToString(d4, 1, 0, "1");
d4 = Decimal4Value::FromDouble(1, 0, -0.499999999, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), 0);
VerifyToString(d4, 1, 0, "0");
d4 = Decimal4Value::FromDouble(1, 0, -0.5, true, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(d4.value(), -1);
VerifyToString(d4, 1, 0, "-1");
}
TEST(DecimalTest, StringToDecimalBasic) {
StringToAllDecimals(" 1234", 10, 0, 1234, StringParser::PARSE_SUCCESS);
StringToAllDecimals("", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals(" ", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals(" 0 0 ", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("0 0", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("++0", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("--0", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("..0", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("0..", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals(".0.", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("0.0.", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("0-", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("0+", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("X", 10, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("+", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("-", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("1234", 10, 0, 1234, StringParser::PARSE_SUCCESS);
StringToAllDecimals("1234", 10, 2, 123400, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-1234", 10, 2, -123400, StringParser::PARSE_SUCCESS);
StringToAllDecimals("123", 2, 0, 123, StringParser::PARSE_OVERFLOW);
StringToAllDecimals(" 12 ", 2, 0, 12, StringParser::PARSE_SUCCESS);
StringToAllDecimals("000", 2, 0, 0, StringParser::PARSE_SUCCESS);
StringToAllDecimals(" . ", 2, 0, 0, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-.", 2, 0, 0, StringParser::PARSE_SUCCESS);
StringToAllDecimals("1.", 2, 0, 1, StringParser::PARSE_SUCCESS);
StringToAllDecimals(".1", 10, 2, 10, StringParser::PARSE_SUCCESS);
StringToAllDecimals("00012.3", 10, 2, 1230, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-00012.3", 10, 2, -1230, StringParser::PARSE_SUCCESS);
StringToAllDecimals("0.00000", 6, 5, 0, StringParser::PARSE_SUCCESS);
StringToAllDecimals("1.00000", 6, 5, 100000, StringParser::PARSE_SUCCESS);
StringToAllDecimals("0.000000", 6, 5, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1.000000", 6, 5, 100000, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1.000004", 6, 5, 100000, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1.000005", 6, 5,
100000, StringParser::PARSE_UNDERFLOW,
100001, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("0.4", 5, 0, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("0.5", 5, 0,
0, StringParser::PARSE_UNDERFLOW,
1, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("123.45", 10, 2, 12345, StringParser::PARSE_SUCCESS);
StringToAllDecimals(".45", 10, 2, 45, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-.45", 10, 2, -45, StringParser::PARSE_SUCCESS);
StringToAllDecimals(" 123.4 ", 10, 5, 12340000, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-123.45", 10, 5, -12345000, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-123.456", 10, 2,
-12345, StringParser::PARSE_UNDERFLOW,
-12346, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("e", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("E", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals("+E", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals(".E", 2, 0, 0, StringParser::PARSE_FAILURE);
StringToAllDecimals(" 0 e 0 ", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("0e 0", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("0e1.0", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("0e1.", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("0e.1", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("0ee0", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("e1", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("1e", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("1.1.0e0", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("1e1.0", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("1..1e1", 10, 0, 100, StringParser::PARSE_FAILURE);
StringToAllDecimals("1e9999999999999999999", 10, 0, 0, StringParser::PARSE_OVERFLOW);
StringToAllDecimals("1e-38", 10, 2, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1e-38", 38, 38, 1, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-1e-38", 38, 38, -1, StringParser::PARSE_SUCCESS);
StringToAllDecimals("1e-39", 38, 38, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("-1e-39", 38, 38, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1e-9999999999999999999", 10, 0, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("-1e-9999999999999999999", 10, 0, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals(" 1e0 ", 10, 2, 100, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-1e0", 10, 2, -100, StringParser::PARSE_SUCCESS);
StringToAllDecimals("1e-0", 10, 2, 100, StringParser::PARSE_SUCCESS);
StringToAllDecimals(" 1e2 ", 10, 2, 10000, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-1e-2", 10, 2, -1, StringParser::PARSE_SUCCESS);
StringToAllDecimals(".011e3 ", 2, 0, 11, StringParser::PARSE_SUCCESS);
StringToAllDecimals(".00011e5 ", 2, 0, 11, StringParser::PARSE_SUCCESS);
StringToAllDecimals("110e-1 ", 2, 0, 11, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1.10e1 ", 2, 0, 11, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("1.10e3 ", 2, 0, 11, StringParser::PARSE_OVERFLOW);
}
TEST(DecimalTest, StringToDecimalLarge) {
StringToAllDecimals("1", 1, 0, 1, StringParser::PARSE_SUCCESS);
StringToAllDecimals("-1", 1, 0, -1, StringParser::PARSE_SUCCESS);
StringToAllDecimals(".1", 1, 0, 0, StringParser::PARSE_UNDERFLOW);
StringToAllDecimals("10", 1, 0, 10, StringParser::PARSE_OVERFLOW);
StringToAllDecimals("-10", 1, 0, -10, StringParser::PARSE_OVERFLOW);
VerifyParse(".1234567890", 10, 10,
Decimal8Value(1234567890L), StringParser::PARSE_SUCCESS);
VerifyParse("-.1234567890", 10, 10,
Decimal8Value(-1234567890L), StringParser::PARSE_SUCCESS);
VerifyParse(".12345678900", 10, 10,
Decimal8Value(1234567890L), StringParser::PARSE_UNDERFLOW);
VerifyParse("-.12345678900", 10, 10,
Decimal8Value(-1234567890L), StringParser::PARSE_UNDERFLOW);
VerifyParse(".1234567890", 10, 10,
Decimal16Value(1234567890L), StringParser::PARSE_SUCCESS);
VerifyParse("-.1234567890", 10, 10,
Decimal16Value(-1234567890L), StringParser::PARSE_SUCCESS);
VerifyParse(".12345678900", 10, 10,
Decimal16Value(1234567890L), StringParser::PARSE_UNDERFLOW);
VerifyParse("-.12345678900", 10, 10,
Decimal16Value(-1234567890L), StringParser::PARSE_UNDERFLOW);
// Up to 8 digits with 5 before the decimal and 3 after.
VerifyParse("12345.678", 8, 3,
Decimal8Value(12345678L), StringParser::PARSE_SUCCESS);
VerifyParse("-12345.678", 8, 3,
Decimal8Value(-12345678L), StringParser::PARSE_SUCCESS);
VerifyParse("123456.78", 8, 3,
Decimal8Value(12345678L), StringParser::PARSE_OVERFLOW);
VerifyParse("1234.5678", 8, 3,
Decimal8Value(1234567L), StringParser::PARSE_UNDERFLOW,
Decimal8Value(1234568L), StringParser::PARSE_UNDERFLOW);
VerifyParse("12345.678", 8, 3,
Decimal16Value(12345678L), StringParser::PARSE_SUCCESS);
VerifyParse("-12345.678", 8, 3,
Decimal16Value(-12345678L), StringParser::PARSE_SUCCESS);
VerifyParse("123456.78", 8, 3,
Decimal16Value(12345678L), StringParser::PARSE_OVERFLOW);
VerifyParse("1234.5678", 8, 3,
Decimal16Value(1234567L), StringParser::PARSE_UNDERFLOW,
Decimal16Value(1234568L), StringParser::PARSE_UNDERFLOW);
// Test max unscaled value for each of the decimal types.
VerifyParse("999999999", 9, 0,
Decimal4Value(999999999), StringParser::PARSE_SUCCESS);
VerifyParse("99999.9999", 9, 4,
Decimal4Value(999999999), StringParser::PARSE_SUCCESS);
VerifyParse("0.999999999", 9, 9,
Decimal4Value(999999999), StringParser::PARSE_SUCCESS);
VerifyParse("-999999999", 9, 0,
Decimal4Value(-999999999), StringParser::PARSE_SUCCESS);
VerifyParse("-99999.9999", 9, 4,
Decimal4Value(-999999999), StringParser::PARSE_SUCCESS);
VerifyParse("-0.999999999", 9, 9,
Decimal4Value(-999999999), StringParser::PARSE_SUCCESS);
VerifyParse("1000000000", 9, 0,
Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("-1000000000", 9, 0,
Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("999999999999999999", 18, 0,
Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse("999999.999999999999", 18, 12,
Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse(".999999999999999999", 18, 18,
Decimal8Value(999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse("-999999999999999999", 18, 0,
Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse("-999999.999999999999", 18, 12,
Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse("-.999999999999999999", 18, 18,
Decimal8Value(-999999999999999999ll), StringParser::PARSE_SUCCESS);
VerifyParse("1000000000000000000", 18, 0,
Decimal8Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("01000000000000000000", 18, 0,
Decimal8Value(0), StringParser::PARSE_OVERFLOW);
int128_t result = DecimalUtil::MAX_UNSCALED_DECIMAL16;
VerifyParse("99999999999999999999999999999999999999",
38, 0, Decimal16Value(result), StringParser::PARSE_SUCCESS);
VerifyParse("99999999999999999999999999999999999999e1",
38, 0, Decimal16Value(result), StringParser::PARSE_OVERFLOW);
VerifyParse("999999999999999999999999999999999999990e-1",
38, 0, Decimal16Value(result), StringParser::PARSE_UNDERFLOW);
VerifyParse("999999999999999999999999999999999.99999",
38, 5, Decimal16Value(result), StringParser::PARSE_SUCCESS);
VerifyParse(".99999999999999999999999999999999999999",
38, 38, Decimal16Value(result), StringParser::PARSE_SUCCESS);
VerifyParse("-99999999999999999999999999999999999999",
38, 0, Decimal16Value(-result), StringParser::PARSE_SUCCESS);
VerifyParse("-999999999999999999999999999999999.99999",
38, 5, Decimal16Value(-result), StringParser::PARSE_SUCCESS);
VerifyParse("-.99999999999999999999999999999999999999",
38, 38, Decimal16Value(-result), StringParser::PARSE_SUCCESS);
VerifyParse("-.99999999999999999999999999999999999999e1",
38, 38, Decimal16Value(-result), StringParser::PARSE_OVERFLOW);
VerifyParse("-.999999999999999999999999999999999999990e-1", 38, 38,
Decimal16Value(-result / 10), StringParser::PARSE_UNDERFLOW,
Decimal16Value(-result / 10 - 1), StringParser::PARSE_UNDERFLOW);
VerifyParse("-.999999999999999999999999999999999999990000000000000000e-20", 38, 38,
Decimal16Value(-result / DecimalUtil::GetScaleMultiplier<int128_t>(20)),
StringParser::PARSE_UNDERFLOW,
Decimal16Value(-result / DecimalUtil::GetScaleMultiplier<int128_t>(20) - 1),
StringParser::PARSE_UNDERFLOW);
VerifyParse("100000000000000000000000000000000000000",
38, 0, Decimal16Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("-100000000000000000000000000000000000000",
38, 0, Decimal16Value(0), StringParser::PARSE_OVERFLOW);
// Rounding tests.
VerifyParse("555.554", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW);
VerifyParse("555.555", 5, 2,
Decimal4Value(55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(55556), StringParser::PARSE_UNDERFLOW);
// Too many digits to the left of the dot so we overflow.
VerifyParse("555.555e1", 5, 2, Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("-555.555e1", 5, 2, Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("5555.555", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("-555.555", 5, 2,
Decimal4Value(-55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(-55556), StringParser::PARSE_UNDERFLOW);
VerifyParse("-5555.555", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("5555.555e-1", 5, 2,
Decimal4Value(55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(55556), StringParser::PARSE_UNDERFLOW);
VerifyParse("55555.555e-1", 5, 2, Decimal16Value(0), StringParser::PARSE_OVERFLOW);
// Too many digits to the right of the dot and not enough to the left. Rounding via
// ScaleDownAndRound().
VerifyParse("5.55444", 5, 2, Decimal4Value(555), StringParser::PARSE_UNDERFLOW);
VerifyParse("5.55555", 5, 2,
Decimal4Value(555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(556), StringParser::PARSE_UNDERFLOW);
VerifyParse("5.555e-9", 5, 2, Decimal4Value(0), StringParser::PARSE_UNDERFLOW);
// The number of digits to the left of the dot equals to precision - scale. Rounding
// by adding 1 if the first truncated digit is greater or equal to 5.
VerifyParse("555.554", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW);
VerifyParse("555.555", 5, 2,
Decimal4Value(55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(55556), StringParser::PARSE_UNDERFLOW);
VerifyParse("5.55554e2", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW);
VerifyParse("5.55555e2", 5, 2,
Decimal4Value(55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(55556), StringParser::PARSE_UNDERFLOW);
VerifyParse("55555.4e-2", 5, 2, Decimal4Value(55555), StringParser::PARSE_UNDERFLOW);
VerifyParse("55555.5e-2", 5, 2,
Decimal4Value(55555), StringParser::PARSE_UNDERFLOW,
Decimal4Value(55556), StringParser::PARSE_UNDERFLOW);
// Rounding causes overflow.
VerifyParse("999.994", 5, 2, Decimal4Value(99999), StringParser::PARSE_UNDERFLOW);
VerifyParse("999.995", 5, 2,
Decimal4Value(99999), StringParser::PARSE_UNDERFLOW,
Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("9.99995e2", 5, 2,
Decimal4Value(99999), StringParser::PARSE_UNDERFLOW,
Decimal4Value(0), StringParser::PARSE_OVERFLOW);
VerifyParse("99999.5e-2", 5, 2,
Decimal4Value(99999), StringParser::PARSE_UNDERFLOW,
Decimal4Value(0), StringParser::PARSE_OVERFLOW);
}
TEST(DecimalTest, Overflow) {
bool overflow = false;
Decimal16Value result;
Decimal16Value d_max(DecimalUtil::MAX_UNSCALED_DECIMAL16);
Decimal16Value two(2);
Decimal16Value one(1);
Decimal16Value zero(0);
// Adding same sign
overflow = false;
d_max.Add<int128_t>(0, one, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
one.Add<int128_t>(0, d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
d_max.Add<int128_t>(0, d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
result = d_max.Add<int128_t>(0, zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value());
// Subtracting same sign
overflow = false;
result = d_max.Subtract<int128_t>(0, one, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
EXPECT_TRUE(result.value() == d_max.value() - 1);
result = one.Subtract<int128_t>(0, d_max, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
EXPECT_TRUE(result.value() == -(d_max.value() - 1));
result = d_max.Subtract<int128_t>(0, d_max, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
overflow = false;
EXPECT_TRUE(result.value() == 0);
result = d_max.Subtract<int128_t>(0, zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value());
// Adding different sign
overflow = false;
result = d_max.Add<int128_t>(0, -one, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value() - 1);
overflow = false;
result = one.Add<int128_t>(0, -d_max, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == -(d_max.value() - 1));
overflow = false;
result = d_max.Add<int128_t>(0, -d_max, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == 0);
overflow = false;
result = d_max.Add<int128_t>(0, -zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value());
// Subtracting different sign
overflow = false;
d_max.Subtract<int128_t>(0, -one, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
one.Subtract<int128_t>(0, -d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
d_max.Subtract<int128_t>(0, -d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
result = d_max.Subtract<int128_t>(0, -zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value());
// Multiply
overflow = false;
result = d_max.Multiply<int128_t>(0, one, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == d_max.value());
overflow = false;
result = d_max.Multiply<int128_t>(0, -one, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == -d_max.value());
overflow = false;
result = d_max.Multiply<int128_t>(0, two, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
result = d_max.Multiply<int128_t>(0, -two, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
result = d_max.Multiply<int128_t>(0, d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
overflow = false;
result = d_max.Multiply<int128_t>(0, -d_max, 0, 38, 0, false, &overflow);
EXPECT_TRUE(overflow);
// Multiply by 0
overflow = false;
result = zero.Multiply<int128_t>(0, one, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == 0);
overflow = false;
result = one.Multiply<int128_t>(0, zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == 0);
overflow = false;
result = zero.Multiply<int128_t>(0, zero, 0, 38, 0, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_TRUE(result.value() == 0);
// Adding any value with scale to (38, 0) will overflow if the most significant
// digit is set.
overflow = false;
result = d_max.Add<int128_t>(0, zero, 1, 38, 1, false, &overflow);
EXPECT_TRUE(overflow);
// Add 37 9's (with scale 0)
Decimal16Value d3(DecimalUtil::MAX_UNSCALED_DECIMAL16 / 10);
overflow = false;
result = d3.Add<int128_t>(0, zero, 1, 38, 1, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(result.value(), DecimalUtil::MAX_UNSCALED_DECIMAL16 - 9);
overflow = false;
result = d3.Add<int128_t>(0, one, 1, 38, 1, false, &overflow);
EXPECT_FALSE(overflow);
EXPECT_EQ(result.value(), DecimalUtil::MAX_UNSCALED_DECIMAL16 - 8);
// Mod
overflow = false;
bool is_nan;
result = d3.Mod<int128_t>(0, d3, 20, 38, 20, false, &is_nan, &overflow);
EXPECT_FALSE(overflow);
EXPECT_FALSE(is_nan);
EXPECT_EQ(result.value(), 0);
overflow = false;
result = d3.Mod<int128_t>(0, two, 0, 38, 0, false, &is_nan, &overflow);
EXPECT_FALSE(overflow);
EXPECT_FALSE(is_nan);
EXPECT_EQ(result.value(), DecimalUtil::MAX_UNSCALED_DECIMAL16 % 2);
result = d3.Mod<int128_t>(0, zero, 1, 38, 1, false, &is_nan, &overflow);
EXPECT_TRUE(is_nan);
}
// Overflow cases only need to test with Decimal16Value with max precision. In
// the other cases, the planner should have casted the values to this precision.
// Add/Subtract/Mod cannot overflow the scale. With division, we always handle the case
// where the result scale needs to be adjusted.
TEST(DecimalTest, MultiplyScaleOverflow) {
bool overflow = false;
Decimal16Value x(1);
Decimal16Value y(3);
int max_scale = 38;
// x = 0.<37 zeroes>1. y = 0.<37 zeroes>3 The result should be 0.<74 zeroes>3.
// Since this can't be represented, the result will truncate to 0.
Decimal16Value result = x.Multiply<int128_t>(max_scale, y, max_scale, 38, 38, false, &overflow);
EXPECT_TRUE(result.value() == 0);
EXPECT_FALSE(overflow);
int scale_1 = 1;
int scale_37 = 37;
// x = 0.<36 zeroes>1, y = 0.3
// The result should be 0.<37 zeroes>11, which would require scale = 39.
// The truncated version should 0.<37 zeroes>3.
result = x.Multiply<int128_t>(scale_37, y, scale_1, 38, 38, false, &overflow);
EXPECT_TRUE(result.value() == 3);
EXPECT_FALSE(overflow);
}
// Test that unaligned decimal values are handled correctly.
TEST(DecimalTest, UnalignedValues) {
// Regression test for IMPALA-7473 that triggered a crash in release builds.
Decimal16Value aligned(12345);
uint8_t* unaligned_mem = reinterpret_cast<uint8_t*>(malloc(sizeof(Decimal16Value) + 9));
memcpy(&unaligned_mem[9], &aligned, sizeof(aligned));
Decimal16Value* unaligned = reinterpret_cast<Decimal16Value*>(&unaligned_mem[9]);
// VerifyToString() worked even prior to the bugfix because GCC happened to generate
// code without aligned load instructions.
VerifyToString(*unaligned, 28, 2, "123.45");
// PrintValue() contained different generated code that wasn't safe for unaligned
// values.
stringstream ss;
RawValue::PrintValue(unaligned, ColumnType::CreateDecimalType(28, 2), 0, &ss);
EXPECT_EQ("123.45", ss.str());
free(unaligned_mem);
}
enum Op {
ADD,
SUBTRACT,
MULTIPLY,
DIVIDE,
MOD,
};
// Implementation of decimal rules. This is handled in the planner in the normal
// execution paths.
ColumnType GetResultType(const ColumnType& t1, const ColumnType& t2, Op op, bool v2) {
// TODO: Implement V2 result types
switch (op) {
case ADD:
case SUBTRACT:
return ColumnType::CreateDecimalType(
max(t1.scale, t2.scale) +
max(t1.precision - t1.scale, t2.precision - t2.scale) + 1,
max(t1.scale, t2.scale));
case MULTIPLY:
return ColumnType::CreateDecimalType(
t1.precision + t2.precision, t1.scale + t2.scale);
case DIVIDE:
if (v2) {
int result_scale =
max(ColumnType::MIN_ADJUSTED_SCALE, t1.scale + t2.precision + 1);
int result_precision =t1.precision - t1.scale + t2.scale + result_scale;
return ColumnType::CreateAdjustedDecimalType(result_precision, result_scale);
} else {
return ColumnType::CreateDecimalType(
min(ColumnType::MAX_PRECISION,
t1.precision - t1.scale + t2.scale + max(4, t1.scale + t2.precision + 1)),
min(ColumnType::MAX_PRECISION, max(4, t1.scale + t2.precision + 1)));
}
case MOD:
return ColumnType::CreateDecimalType(
min(t1.precision - t1.scale, t2.precision - t2.scale) + max(t1.scale, t2.scale),
max(t1.scale, t2.scale));
default:
EXPECT_TRUE(false);
return ColumnType();
}
}
TEST(DecimalTest, ResultTypes) {
ColumnType t1 = ColumnType::CreateDecimalType(38, 10);
ColumnType t2 = ColumnType::CreateDecimalType(38, 38);
ColumnType t3 = ColumnType::CreateDecimalType(38, 0);
auto r1 = GetResultType(t1, t2, DIVIDE, true);
EXPECT_EQ(r1.precision, 38);
EXPECT_EQ(r1.scale, 6);
auto r2 = GetResultType(t1, t3, DIVIDE, true);
EXPECT_EQ(r2.precision, 38);
EXPECT_EQ(r2.scale, 10);
auto r3 = GetResultType(t1, t2, DIVIDE, false);
EXPECT_EQ(r3.precision, 38);
EXPECT_EQ(r3.scale, 38);
}
template<typename T>
void VerifyFuzzyEquals(const T& actual, const ColumnType& t,
double expected, bool overflow, double max_error = MAX_ERROR) {
double actual_d = actual.ToDouble(t.scale);
EXPECT_FALSE(overflow);
EXPECT_TRUE(fabs(actual_d - expected) < max_error)
<< actual_d << " != " << expected;
}
TEST(DecimalTest, BasicArithmetic) {
ColumnType t1 = ColumnType::CreateDecimalType(5, 4);
ColumnType t2 = ColumnType::CreateDecimalType(8, 3);
ColumnType t1_plus_2 = GetResultType(t1, t2, ADD, false);
ColumnType t1_times_2 = GetResultType(t1, t2, MULTIPLY, false);
Decimal4Value d1(123456789);
Decimal4Value d2(23456);
Decimal4Value d3(-23456);
double d1_double = d1.ToDouble(t1.scale);
double d2_double = d2.ToDouble(t2.scale);
double d3_double = d3.ToDouble(t2.scale);
bool overflow = false;
// TODO: what's the best way to author a bunch of tests like this?
VerifyFuzzyEquals(d1.Add<int64_t>(
t1.scale, d2, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow),
t1_plus_2, d1_double + d2_double, overflow);
VerifyFuzzyEquals(d1.Add<int64_t>(
t1.scale, d3, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow),
t1_plus_2, d1_double + d3_double, overflow);
VerifyFuzzyEquals(d1.Subtract<int64_t>(
t1.scale, d2, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow),
t1_plus_2, d1_double - d2_double, overflow);
VerifyFuzzyEquals(d1.Subtract<int64_t>(
t1.scale, d3, t2.scale, t1_plus_2.precision, t1_plus_2.scale, false, &overflow),
t1_plus_2, d1_double - d3_double, overflow);
VerifyFuzzyEquals(d1.Multiply<int128_t>(
t1.scale, d2, t2.scale, t1_times_2.precision, t1_times_2.scale, false, &overflow),
t1_times_2, d1_double * d2_double, overflow);
VerifyFuzzyEquals(d1.Multiply<int64_t>(
t1.scale, d3, t2.scale, t1_times_2.precision, t1_times_2.scale, false, &overflow),
t1_times_2, d1_double * d3_double, overflow);
}
TEST(DecimalTest, Divide) {
// Exhaustively test precision and scale for 4 byte decimals. The logic errors tend
// to be by powers of 10 so not testing the other decimal types is okay.
Decimal4Value x(123456789);
Decimal4Value y(234);
for (int numerator_p = 1; numerator_p <= 9; ++numerator_p) {
for (int numerator_s = 0; numerator_s <= numerator_p; ++numerator_s) {
for (int denominator_p = 1; denominator_p <= 3; ++denominator_p) {
for (int denominator_s = 0; denominator_s <= denominator_p; ++denominator_s) {
for (int v2: { 0, 1 }) {
ColumnType t1 = ColumnType::CreateDecimalType(numerator_p, numerator_s);
ColumnType t2 = ColumnType::CreateDecimalType(denominator_p, denominator_s);
ColumnType t3 = GetResultType(t1, t2, DIVIDE, v2);
bool is_nan = false;
bool is_overflow = false;
Decimal8Value r = x.Divide<int64_t>(
t1.scale, y, t2.scale, t3.precision, t3.scale, true, &is_nan, &is_overflow);
double approx_x = x.ToDouble(t1.scale);
double approx_y = y.ToDouble(t2.scale);
double approx_r = r.ToDouble(t3.scale);
double expected_r = approx_x / approx_y;
EXPECT_FALSE(is_nan);
EXPECT_FALSE(is_overflow);
if (fabs(approx_r - expected_r) > MAX_ERROR) {
LOG(ERROR) << approx_r << " " << expected_r;
LOG(ERROR) << x.ToString(t1) << "/" << y.ToString(t2)
<< "=" << r.ToString(t3);
EXPECT_TRUE(false);
}
}
}
}
}
}
// Divide by 0
bool is_nan = false;
bool is_overflow = false;
Decimal8Value r = x.Divide<int64_t>(0, Decimal4Value(0), 0, 38, 4, true,
&is_nan, &is_overflow);
EXPECT_TRUE(is_nan) << "Expected NaN, got: " << r;
EXPECT_FALSE(is_overflow);
// In this case, we are dividing large precision decimals meaning the resulting
// decimal underflows. The resulting type is (38,38).
Decimal16Value x2(53994500);
Decimal16Value y2(5399450);
is_nan = false;
is_overflow = false;
x2.Divide<int128_t>(4, y2, 4, 38, 38, true, &is_nan, &is_overflow);
EXPECT_TRUE(is_overflow);
EXPECT_FALSE(is_nan);
}
TEST(DecimalTest, DivideLargeScales) {
ColumnType t1 = ColumnType::CreateDecimalType(38, 8);
ColumnType t2 = ColumnType::CreateDecimalType(20, 0);
ColumnType t3 = GetResultType(t1, t2, DIVIDE, false);
StringParser::ParseResult result;
const char* data = "319391280635.61476055";
Decimal16Value x =
StringParser::StringToDecimal<int128_t>(data, strlen(data), t1, false, &result);
Decimal16Value y(10000);
bool is_nan = false;
bool is_overflow = false;
Decimal16Value r = x.Divide<int128_t>(t1.scale, y, t2.scale, t3.precision, t3.scale,
true, &is_nan, &is_overflow);
VerifyToString(r, t3.precision, t3.scale, "31939128.06356147605500000000000000000");
EXPECT_FALSE(is_nan);
EXPECT_FALSE(is_overflow);
y = -y;
r = x.Divide<int128_t>(t1.scale, y, t2.scale, t3.precision, t3.scale, true,
&is_nan, &is_overflow);
VerifyToString(r, t3.precision, t3.scale, "-31939128.06356147605500000000000000000");
EXPECT_FALSE(is_nan);
EXPECT_FALSE(is_overflow);
}
template<typename T>
DecimalValue<T> RandDecimal(int max_precision) {
T val = 0;
int precision = rand() % max_precision;
for (int i = 0; i < precision; ++i) {
int digit = rand() % 10;
val = val * 10 + digit;
}
return DecimalValue<T>(rand() % 2 == 0 ? val : -val);
}
int DoubleCompare(double x, double y) {
if (x < y) return -1;
if (x > y) return 1;
return 0;
}
// Randomly test decimal operations, comparing the result with a double ground truth.
TEST(DecimalTest, RandTesting) {
int NUM_ITERS = 1000000;
int seed = time(0);
LOG(ERROR) << "Seed: " << seed;
for (int i = 0; i < NUM_ITERS; ++i) {
// TODO: double is too imprecise so we can't test with high scales.
int p1 = rand() % 12 + 1;
int s1 = rand() % min(4, p1);
int p2 = rand() % 12 + 1;
int s2 = rand() % min(4, p2);
DecimalValue<int64_t> dec1 = RandDecimal<int64_t>(p1);
DecimalValue<int64_t> dec2 = RandDecimal<int64_t>(p2);
ColumnType t1 = ColumnType::CreateDecimalType(p1, s1);
ColumnType t2 = ColumnType::CreateDecimalType(p2, s2);
double t1_d = dec1.ToDouble(s1);
double t2_d = dec2.ToDouble(s2);
ColumnType add_t = GetResultType(t1, t2, ADD, false);
bool overflow = false;
VerifyFuzzyEquals(dec1.Add<int64_t>(
t1.scale, dec2, t2.scale, add_t.precision, add_t.scale, false, &overflow),
add_t, t1_d + t2_d, overflow);
VerifyFuzzyEquals(dec1.Subtract<int64_t>(
t1.scale, dec2, t2.scale, add_t.precision, add_t.scale, false, &overflow),
add_t, t1_d - t2_d, overflow);
if (overflow) continue;
#if 0
// multiply V2 result type not implemented yet
ColumnType multiply_t = GetResultType(t1, t2, MULTIPLY, true);
VerifyFuzzyEquals(dec1.Multiply<int64_t>(
t1.scale, dec2, t2.scale, multiply_t.scale), multiply_t, t1_d * t2_d);
#endif
// With rounding, we should be able to get much closer to real values
ColumnType divide_t = GetResultType(t1, t2, DIVIDE, true);
bool is_nan = false;
overflow = false;
auto result = dec1.Divide<int128_t>(t1.scale, dec2, t2.scale, divide_t.precision,
divide_t.scale, true, &is_nan, &overflow);
if (!is_nan && !overflow)
VerifyFuzzyEquals(result, divide_t, t1_d / t2_d, false,
pow(10, divide_t.precision - divide_t.scale - 6));
EXPECT_EQ(is_nan, dec2.value() == 0);
EXPECT_EQ(dec1.Compare(t1.scale, dec2, t2.scale), DoubleCompare(t1_d, t2_d));
EXPECT_TRUE(dec1.Compare(t1.scale, dec1, t1.scale) == 0);
EXPECT_TRUE(dec2.Compare(t2.scale, dec2, t2.scale) == 0);
}
}
TEST(DecimalTest, PrecisionScaleValidation) {
// Valid precision and scale.
EXPECT_TRUE(ColumnType::ValidateDecimalParams(1, 0));
EXPECT_TRUE(ColumnType::ValidateDecimalParams(1, 1));
EXPECT_TRUE(ColumnType::ValidateDecimalParams(38, 38));
EXPECT_TRUE(ColumnType::ValidateDecimalParams(38, 0));
// Out of range precision or scale.
EXPECT_FALSE(ColumnType::ValidateDecimalParams(3, -1));
EXPECT_FALSE(ColumnType::ValidateDecimalParams(0, 0));
EXPECT_FALSE(ColumnType::ValidateDecimalParams(39, 0));
EXPECT_FALSE(ColumnType::ValidateDecimalParams(38, 39));
// Incompatible precision and scale.
EXPECT_FALSE(ColumnType::ValidateDecimalParams(15, 16));
}
}