Skip to content

Commit

Permalink
fix: correct decimal precision from literals.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Jul 4, 2024
1 parent eaa8369 commit cf78577
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 28 deletions.
30 changes: 17 additions & 13 deletions src/mizugaki/analyzer/details/analyze_literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,23 @@ class engine {
auto max_precision = static_cast<mpd_ssize_t>(context_.options()->max_decimal_precision());
::decimal::Context context {
max_precision,
max_precision, // emax
-max_precision, // emin
MPD_MAX_EMAX, // emax
MPD_MIN_EMIN, // emin
// FIXME: other decimal context
};

::decimal::Decimal v { *value.unsigned_value(), context };
if ((context.status() & ::decimal::DecRounded) != 0U ||
v.exponent() > 0 ||
v.exponent() < -max_precision) {
context_.report(sql_analyzer_code::unsupported_decimal_value,
string_builder {}
<< "invalid decimal value '" << value.unsigned_value() << "'"
<< " (inexact value by rounding)"
<< string_builder::to_string,
value.region());
return {};
}
if (value.sign() == ast::literal::sign::minus) {
v = v.minus(context);
}
Expand All @@ -212,15 +223,6 @@ class engine {
<< string_builder::to_string,
value.region());
}
// SQL does not support number with positive exponent
if (v.exponent() > 0) {
context_.report(sql_analyzer_code::unsupported_decimal_value,
string_builder {}
<< "invalid decimal value '" << value.unsigned_value() << "'"
<< string_builder::to_string,
value.region());
return {};
}
::takatori::decimal::triple triple { v };
if (triple.exponent() == 0) {
static constexpr auto positive_max = static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max());
Expand Down Expand Up @@ -262,16 +264,18 @@ class engine {
context_.types().get(ttype::int8 {}));
}
}
auto scale = static_cast<std::size_t>(-v.exponent());
std::optional<std::size_t> precision {};
if (context_.options()->prefer_small_decimal_literals()) {
precision = static_cast<std::size_t>(::decimal::Decimal::radix());
auto digits = static_cast<std::size_t>(v.coeff().adjexp() + 1);
precision = std::max(digits, scale);
}
return context_.create<tscalar::immediate>(
value.region(),
context_.values().get(tvalue::decimal { ::takatori::decimal::triple { v } }),
context_.types().get(ttype::decimal {
precision,
static_cast<std::size_t>(v.exponent()),
scale,
}));
}

Expand Down
176 changes: 162 additions & 14 deletions test/mizugaki/analyzer/details/analyze_literal_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ class analyze_literal_test : public test_parent {

void invalid(ast::literal::literal const& literal) {
auto r = analyze_literal(context(), literal);
EXPECT_FALSE(r);
if (r) {
ADD_FAILURE() << *r;
}
EXPECT_NE(count_error(), 0);
}

Expand Down Expand Up @@ -129,6 +131,7 @@ TEST_F(analyze_literal_test, unsigned_int8) {
}

TEST_F(analyze_literal_test, unsigned_mpint) {
options_.prefer_small_decimal_literals() = true;
auto r = analyze_literal(
context(),
ast::literal::numeric {
Expand All @@ -138,11 +141,12 @@ TEST_F(analyze_literal_test, unsigned_mpint) {
ASSERT_TRUE(r);
EXPECT_EQ(*r, (tscalar::immediate {
tvalue::decimal { "1234567890123456789012345" },
ttype::decimal { 25 },
ttype::decimal { 25, 0 },
}));
}

TEST_F(analyze_literal_test, unsigned_decimal) {
options_.prefer_small_decimal_literals() = true;
auto r = analyze_literal(
context(),
ast::literal::numeric {
Expand Down Expand Up @@ -335,7 +339,7 @@ TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_uint64) {

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_max) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = false;
options_.prefer_small_decimal_literals() = true;
std::string s9x38;
s9x38.resize(38, '9');

Expand All @@ -349,7 +353,7 @@ TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_max) {
0,
}
},
ttype::decimal { {}, 0 },
ttype::decimal { 38, 0 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, s9x38),
(tscalar::immediate {
Expand All @@ -361,29 +365,29 @@ TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_max) {
0,
}
},
ttype::decimal { {}, 0 },
ttype::decimal { 38, 0 },
}));
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_exponents) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = false;
options_.prefer_small_decimal_literals() = true;
std::string s9x39;
s9x39.resize(39, '9');
for (int dot_position = 0; dot_position < static_cast<int>(s9x39.size()); ++dot_position) {
for (int scale = 0; scale < static_cast<int>(s9x39.size()); ++scale) {
auto unsigned_value = s9x39;
unsigned_value[s9x39.size() - (dot_position + 1)] = '.';
unsigned_value[s9x39.size() - (scale + 1)] = '.';
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::plus, unsigned_value),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
+1,
5421010862427522170ULL, // coef-hi
687399551400673279ULL, // coef-lo
-dot_position,
-scale,
}
},
ttype::decimal { {}, 0 },
ttype::decimal { 38, scale },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, unsigned_value),
(tscalar::immediate {
Expand All @@ -392,17 +396,134 @@ TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_exponents) {
-1,
5421010862427522170ULL, // coef-hi
687399551400673279ULL, // coef-lo
-dot_position,
-scale,
}
},
ttype::decimal { {}, 0 },
ttype::decimal { 38, scale },
}));
}
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_over) {
TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_min) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = true;
std::string p38;
p38.resize(38, '0');
p38[37] = '1'; // 0{37}1
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::plus, "." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
+1,
0ULL, // coef-hi
1ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, "." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
-1,
0ULL, // coef-hi
1ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::plus, "0." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
+1,
0ULL, // coef-hi
1ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, "0." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
-1,
0ULL, // coef-hi
1ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_leading_zeros) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = true;
std::string p38;
p38.resize(38, '0'); // 0{38}
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::plus, "." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
+1,
0ULL, // coef-hi
0ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, "." + p38),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
-1,
0ULL, // coef-hi
0ULL, // coef-lo
-38,
}
},
ttype::decimal { 38, 38 },
}));
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_one_leading_zeros) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = true;
std::string p37;
p37.resize(37, '0'); // 0{37}
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::plus, "1." + p37),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
+1,
542101086242752217ULL, // coef-hi
68739955140067328ULL, // coef-lo
-37,
}
},
ttype::decimal { 38, 37 },
}));
EXPECT_EQ(*parse_exact_numeric(ast::literal::sign::minus, "1." + p37),
(tscalar::immediate {
tvalue::decimal {
tvalue::decimal::entity_type {
-1,
542101086242752217ULL, // coef-hi
68739955140067328ULL, // coef-lo
-37,
}
},
ttype::decimal { 38, 37 },
}));
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_overflow) {
options_.max_decimal_precision() = 38;
options_.prefer_small_decimal_literals() = false;
std::string p39;
p39.resize(39, '0');
p39[0] = '1'; // 10{38}
Expand All @@ -418,6 +539,30 @@ TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_over) {
});
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_rounding) {
options_.max_decimal_precision() = 38;
std::string p38;
p38.resize(38, '0');
p38[0] = '1'; // 10^37
invalid(sql_analyzer_code::unsupported_decimal_value, ast::literal::numeric {
ast::literal::kind::exact_numeric,
{},
{ p38 + ".0" },
});
}

TEST_F(analyze_literal_test, exact_numeric_decimal38_boundary_undeflow) {
options_.max_decimal_precision() = 38;
std::string p39;
p39.resize(39, '0');
p39[38] = '1'; // 0{38}1
invalid(sql_analyzer_code::unsupported_decimal_value, ast::literal::numeric {
ast::literal::kind::exact_numeric,
{},
{ "." + p39 },
});
}

TEST_F(analyze_literal_test, unsigned_approx_numeric) {
auto r = analyze_literal(
context(),
Expand Down Expand Up @@ -448,6 +593,7 @@ TEST_F(analyze_literal_test, signed_approx_numeric) {
}

TEST_F(analyze_literal_test, character_string) {
options_.prefer_small_character_literals() = true;
auto r = analyze_literal(
context(),
ast::literal::string {
Expand All @@ -462,6 +608,7 @@ TEST_F(analyze_literal_test, character_string) {
}

TEST_F(analyze_literal_test, character_string_quote) {
options_.prefer_small_character_literals() = true;
auto r = analyze_literal(
context(),
ast::literal::string {
Expand All @@ -476,6 +623,7 @@ TEST_F(analyze_literal_test, character_string_quote) {
}

TEST_F(analyze_literal_test, character_string_concat) {
options_.prefer_small_character_literals() = true;
auto r = analyze_literal(
context(),
ast::literal::string {
Expand Down
2 changes: 1 addition & 1 deletion test/mizugaki/analyzer/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ inline tscalar::immediate immediate(tvalue::int8::entity_type value) {
inline tscalar::immediate immediate(std::string_view value) {
return tscalar::immediate {
tvalue::character { value },
ttype::character { ttype::varying, value.size() },
ttype::character { ttype::varying, {} },
};
}

Expand Down

0 comments on commit cf78577

Please sign in to comment.