diff --git a/src/IO/ReadHelpers.h b/src/IO/ReadHelpers.h index 9003ec2d9ed..a772d4ccd69 100644 --- a/src/IO/ReadHelpers.h +++ b/src/IO/ReadHelpers.h @@ -270,16 +270,37 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf) } const size_t initial_pos = buf.count(); + bool has_sign = false; + bool has_number = false; while (!buf.eof()) { switch (*buf.position()) { case '+': { + if (has_sign || has_number) + { + if constexpr (throw_exception) + throw ParsingException( + "Cannot parse number with multiple sign (+/-) characters or intermediate sign character", + ErrorCodes::CANNOT_PARSE_NUMBER); + else + return ReturnType(false); + } + has_sign = true; break; } case '-': { + if (has_sign || has_number) + { + if constexpr (throw_exception) + throw ParsingException( + "Cannot parse number with multiple sign (+/-) characters or intermediate sign character", + ErrorCodes::CANNOT_PARSE_NUMBER); + else + return ReturnType(false); + } if constexpr (is_signed_v) negative = true; else @@ -289,6 +310,7 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf) else return ReturnType(false); } + has_sign = true; break; } case '0': [[fallthrough]]; @@ -302,6 +324,7 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf) case '8': [[fallthrough]]; case '9': { + has_number = true; if constexpr (check_overflow == ReadIntTextCheckOverflow::CHECK_OVERFLOW && !is_big_int_v) { /// Perform relativelly slow overflow check only when @@ -330,6 +353,14 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf) } end: + if (has_sign && !has_number) + { + if constexpr (throw_exception) + throw ParsingException( + "Cannot parse number with a sign character but without any numeric character", ErrorCodes::CANNOT_PARSE_NUMBER); + else + return ReturnType(false); + } x = res; if constexpr (is_signed_v) { diff --git a/tests/performance/string_to_int.xml b/tests/performance/string_to_int.xml new file mode 100644 index 00000000000..f231a4554d4 --- /dev/null +++ b/tests/performance/string_to_int.xml @@ -0,0 +1,12 @@ + + DROP TABLE IF EXISTS numeric_strings + CREATE TABLE numeric_strings(num String) ENGINE Memory + + + INSERT INTO numeric_strings SELECT number FROM numbers(30000000) + + + SELECT count(num::Int64) FROM numeric_strings FORMAT Null + + DROP TABLE IF EXISTS numeric_strings + diff --git a/tests/queries/0_stateless/00574_empty_strings_deserialization.sh b/tests/queries/0_stateless/00574_empty_strings_deserialization.sh index 1cbc9456ab0..fecb6ee17e6 100755 --- a/tests/queries/0_stateless/00574_empty_strings_deserialization.sh +++ b/tests/queries/0_stateless/00574_empty_strings_deserialization.sh @@ -8,8 +8,8 @@ $CLICKHOUSE_CLIENT -q "DROP TABLE IF EXISTS empty_strings_deserialization" $CLICKHOUSE_CLIENT -q "CREATE TABLE empty_strings_deserialization(s String, i Int32, f Float32) ENGINE Memory" echo ',,' | $CLICKHOUSE_CLIENT -q "INSERT INTO empty_strings_deserialization FORMAT CSV" -echo 'aaa,-,' | $CLICKHOUSE_CLIENT -q "INSERT INTO empty_strings_deserialization FORMAT CSV" -echo 'bbb,,-' | $CLICKHOUSE_CLIENT -q "INSERT INTO empty_strings_deserialization FORMAT CSV" +echo 'aaa,,' | $CLICKHOUSE_CLIENT -q "INSERT INTO empty_strings_deserialization FORMAT CSV" +echo 'bbb,,-0' | $CLICKHOUSE_CLIENT -q "INSERT INTO empty_strings_deserialization FORMAT CSV" $CLICKHOUSE_CLIENT -q "SELECT * FROM empty_strings_deserialization ORDER BY s" diff --git a/tests/queries/0_stateless/01425_decimal_parse_big_negative_exponent.sql b/tests/queries/0_stateless/01425_decimal_parse_big_negative_exponent.sql index 7f276d1f8d4..7d0993c1bfc 100644 --- a/tests/queries/0_stateless/01425_decimal_parse_big_negative_exponent.sql +++ b/tests/queries/0_stateless/01425_decimal_parse_big_negative_exponent.sql @@ -1,4 +1,4 @@ -SELECT '-1E9-1E9-1E9-1E9' AS x, toDecimal32(x, 0); -- { serverError 6 } +SELECT '-1E9-1E9-1E9-1E9' AS x, toDecimal32(x, 0); -- { serverError 72 } SELECT '-1E9' AS x, toDecimal32(x, 0); -- { serverError 69 } SELECT '1E-9' AS x, toDecimal32(x, 0); SELECT '1E-8' AS x, toDecimal32(x, 0); diff --git a/tests/queries/0_stateless/01888_read_int_safe.reference b/tests/queries/0_stateless/01888_read_int_safe.reference new file mode 100644 index 00000000000..94d36aad185 --- /dev/null +++ b/tests/queries/0_stateless/01888_read_int_safe.reference @@ -0,0 +1,2 @@ +1 +-1 diff --git a/tests/queries/0_stateless/01888_read_int_safe.sql b/tests/queries/0_stateless/01888_read_int_safe.sql new file mode 100644 index 00000000000..3caa4878aba --- /dev/null +++ b/tests/queries/0_stateless/01888_read_int_safe.sql @@ -0,0 +1,10 @@ +select toInt64('--1'); -- { serverError 72; } +select toInt64('+-1'); -- { serverError 72; } +select toInt64('++1'); -- { serverError 72; } +select toInt64('++'); -- { serverError 72; } +select toInt64('+'); -- { serverError 72; } +select toInt64('1+1'); -- { serverError 72; } +select toInt64('1-1'); -- { serverError 72; } +select toInt64(''); -- { serverError 32; } +select toInt64('1'); +select toInt64('-1');