From dd0848008c9d42f6cb0338e58aebc3f35065cd3f Mon Sep 17 00:00:00 2001 From: Suguru ARAKAWA Date: Tue, 2 Jul 2024 09:27:18 +0900 Subject: [PATCH] fix: better error handling around type analysis. --- include/mizugaki/analyzer/sql_analyzer_code.h | 6 +- .../analyzer/details/analyze_type.cpp | 48 +++++- .../analyzer/details/analyze_type_test.cpp | 145 ++++++++++++++++++ 3 files changed, 193 insertions(+), 6 deletions(-) diff --git a/include/mizugaki/analyzer/sql_analyzer_code.h b/include/mizugaki/analyzer/sql_analyzer_code.h index 93cae6a..0f95893 100644 --- a/include/mizugaki/analyzer/sql_analyzer_code.h +++ b/include/mizugaki/analyzer/sql_analyzer_code.h @@ -37,8 +37,8 @@ enum class sql_analyzer_code { // about types /// @brief the flexible length or precision is not supported in this type. flexible_length_is_not_supported, - /// @brief the type length or precision is too large. - type_length_is_too_large, + /// @brief the type length or precision is not valid. + invalid_type_length, /// @brief the numeric scale is too large for the precision. invalid_numeric_scale, @@ -137,7 +137,7 @@ inline constexpr std::string_view to_string_view(sql_analyzer_code value) noexce case kind::unsupported_string_value: return "unsupported_string_value"sv; case kind::flexible_length_is_not_supported: return "flexible_length_is_not_supported"sv; - case kind::type_length_is_too_large: return "type_length_is_too_large"sv; + case kind::invalid_type_length: return "invalid_type_length"sv; case kind::invalid_numeric_scale: return "invalid_numeric_scale"sv; case kind::schema_not_found: return "schema_not_found"sv; diff --git a/src/mizugaki/analyzer/details/analyze_type.cpp b/src/mizugaki/analyzer/details/analyze_type.cpp index a0d3713..5eb6125 100644 --- a/src/mizugaki/analyzer/details/analyze_type.cpp +++ b/src/mizugaki/analyzer/details/analyze_type.cpp @@ -93,6 +93,13 @@ class engine { } } else if (auto len = type.length()) { size = type.length().value().value(); + if (size == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "character type must not be empty", + type.length().value().region()); + return {}; + } } else if (!type.is_varying()) { size = 1; } @@ -119,6 +126,13 @@ class engine { } } else if (auto len = type.length()) { size = type.length().value().value(); + if (size == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "bit type must not be empty", + type.length().value().region()); + return {}; + } } else if (!type.is_varying()) { size = 1; } @@ -145,6 +159,13 @@ class engine { } } else if (auto len = type.length()) { size = type.length().value().value(); + if (size == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "binary type must not be empty", + type.length().value().region()); + return {}; + } } else if (!type.is_varying()) { size = 1; } @@ -175,9 +196,16 @@ class engine { scale = **type.scale(); } } + if (precision && precision == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "decimal type precision must not be zero", + type.precision().value().region()); + return {}; + } if (precision && precision > options().max_decimal_precision()) { context_.report( - sql_analyzer_code::type_length_is_too_large, + sql_analyzer_code::invalid_type_length, string_builder {} << "too large decimal precision: " << precision.value() << " (max precision is " << options().max_decimal_precision() << ")" @@ -210,6 +238,13 @@ class engine { if (type.is_flexible_precision() || !prec) { return build(type, ttype::int8 {}); } + if (**prec == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "integer type precision must not be zero", + type.precision().value().region()); + return {}; + } if (**prec <= options().max_binary_integer1_precision()) { return build(type, ttype::int1 {}); } @@ -223,7 +258,7 @@ class engine { return build(type, ttype::int8 {}); } context_.report( - sql_analyzer_code::type_length_is_too_large, + sql_analyzer_code::invalid_type_length, string_builder {} << "too large integer precision: " << **prec << " (max precision is " << options().max_binary_integer8_precision() << ")" @@ -235,6 +270,13 @@ class engine { if (type.is_flexible_precision() || !prec) { return build(type, ttype::float8 {}); } + if (**prec == 0) { + context_.report( + sql_analyzer_code::invalid_type_length, + "float type precision must not be zero", + type.precision().value().region()); + return {}; + } if (**prec <= options().max_binary_float4_precision()) { return build(type, ttype::float4 {}); } @@ -242,7 +284,7 @@ class engine { return build(type, ttype::float8 {}); } context_.report( - sql_analyzer_code::type_length_is_too_large, + sql_analyzer_code::invalid_type_length, string_builder {} << "too large float precision: " << **prec << " (max precision is " << options().max_binary_float8_precision() << ")" diff --git a/test/mizugaki/analyzer/details/analyze_type_test.cpp b/test/mizugaki/analyzer/details/analyze_type_test.cpp index 8376522..e6d7cc2 100644 --- a/test/mizugaki/analyzer/details/analyze_type_test.cpp +++ b/test/mizugaki/analyzer/details/analyze_type_test.cpp @@ -74,6 +74,17 @@ TEST_F(analyze_type_test, character_flexible) { EXPECT_TRUE(find_error(error_code::flexible_length_is_not_supported)); } +TEST_F(analyze_type_test, character_length_zero) { + auto r = analyze_type( + context(), + ast::type::character_string { + ast::type::kind::character, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, character_varying) { auto r = analyze_type( context(), @@ -106,6 +117,17 @@ TEST_F(analyze_type_test, character_varying_flexible) { EXPECT_EQ(*r, (ttype::character { ttype::varying, std::nullopt })); } +TEST_F(analyze_type_test, character_varying_length_zero) { + auto r = analyze_type( + context(), + ast::type::character_string { + ast::type::kind::character_varying, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, bit) { auto r = analyze_type( context(), @@ -138,6 +160,17 @@ TEST_F(analyze_type_test, bit_flexible) { EXPECT_TRUE(find_error(error_code::flexible_length_is_not_supported)); } +TEST_F(analyze_type_test, bit_length_zero) { + auto r = analyze_type( + context(), + ast::type::bit_string { + ast::type::kind::bit, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, bit_varying) { auto r = analyze_type( context(), @@ -170,6 +203,17 @@ TEST_F(analyze_type_test, bit_varying_flexible) { EXPECT_EQ(*r, (ttype::bit { ttype::varying, std::nullopt })); } +TEST_F(analyze_type_test, bit_varying_length_zero) { + auto r = analyze_type( + context(), + ast::type::bit_string { + ast::type::kind::bit_varying, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, octet) { auto r = analyze_type( context(), @@ -202,6 +246,17 @@ TEST_F(analyze_type_test, octet_flexible) { EXPECT_TRUE(find_error(error_code::flexible_length_is_not_supported)); } +TEST_F(analyze_type_test, octet_length_zero) { + auto r = analyze_type( + context(), + ast::type::octet_string { + ast::type::kind::octet, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, octet_varying) { auto r = analyze_type( context(), @@ -234,6 +289,17 @@ TEST_F(analyze_type_test, octet_varying_flexible) { EXPECT_EQ(*r, (ttype::octet { ttype::varying, std::nullopt })); } +TEST_F(analyze_type_test, octet_varying_length_zero) { + auto r = analyze_type( + context(), + ast::type::octet_string { + ast::type::kind::octet_varying, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, numeric) { options_.default_decimal_precision() = 20; auto r = analyze_type( @@ -315,6 +381,41 @@ TEST_F(analyze_type_test, decimal_flexible_with_scale) { EXPECT_EQ(*r, (ttype::decimal { {}, 2, })); } +TEST_F(analyze_type_test, decimal_precision_zero) { + auto r = analyze_type( + context(), + ast::type::decimal { + ast::type::kind::decimal, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + +TEST_F(analyze_type_test, decimal_precision_too_large) { + options_.max_decimal_precision() = 20; + auto r = analyze_type( + context(), + ast::type::decimal { + ast::type::kind::decimal, + 21, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + +TEST_F(analyze_type_test, decimal_scale_too_large) { + auto r = analyze_type( + context(), + ast::type::decimal { + ast::type::kind::decimal, + 10, + 11, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_numeric_scale)); +} + TEST_F(analyze_type_test, tiny_integer) { auto r = analyze_type( context(), @@ -440,6 +541,28 @@ TEST_F(analyze_type_test, integer_flexible) { EXPECT_EQ(*r, (ttype::int8 {})); } +TEST_F(analyze_type_test, binary_numeric_length_zero) { + auto r = analyze_type( + context(), + ast::type::binary_numeric { + ast::type::kind::binary_integer, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + +TEST_F(analyze_type_test, binary_numeric_length_too_large) { + auto r = analyze_type( + context(), + ast::type::binary_numeric { + ast::type::kind::binary_integer, + options_.max_binary_integer8_precision() + 1, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, float_single) { auto r = analyze_type( context(), @@ -473,6 +596,28 @@ TEST_F(analyze_type_test, float_flexible) { EXPECT_EQ(*r, (ttype::float8 {})); } +TEST_F(analyze_type_test, binary_float_length_zero) { + auto r = analyze_type( + context(), + ast::type::binary_numeric { + ast::type::kind::binary_float, + 0, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + +TEST_F(analyze_type_test, binary_float_length_too_large) { + auto r = analyze_type( + context(), + ast::type::binary_numeric { + ast::type::kind::binary_float, + options_.max_binary_float8_precision() + 1, + }); + EXPECT_FALSE(r); + EXPECT_TRUE(find_error(sql_analyzer_code::invalid_type_length)); +} + TEST_F(analyze_type_test, boolean) { auto r = analyze_type( context(),