Skip to content

Commit

Permalink
Internal duckdb#2017: VARCHAR DECIMAL Limits
Browse files Browse the repository at this point in the history
Add a limit check in casting VARCHAR to DECIMAL to make sure
the rounding didn't overflow.

fixes: duckdblabs/duckdb-internal#2017
  • Loading branch information
hawkfish committed May 28, 2024
1 parent 3e33613 commit 3aadb51
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
24 changes: 23 additions & 1 deletion src/include/duckdb/common/operator/decimal_cast_operators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,21 @@ string_t StringCastFromDecimal::Operation(hugeint_t input, uint8_t width, uint8_
//===--------------------------------------------------------------------===//
enum class ExponentType : uint8_t { NONE, POSITIVE, NEGATIVE };

template <typename T>
struct DecimalCastTraits {
using POWERS_OF_TEN_CLASS = NumericHelper;
};

template <>
struct DecimalCastTraits<hugeint_t> {
using POWERS_OF_TEN_CLASS = Hugeint;
};

template <>
struct DecimalCastTraits<uhugeint_t> {
using POWERS_OF_TEN_CLASS = Uhugeint;
};

template <class T>
struct DecimalCastData {
using StoreType = T;
Expand All @@ -479,6 +494,7 @@ struct DecimalCastData {
//! Only set when ALLOW_EXPONENT is enabled
uint8_t excessive_decimals;
ExponentType exponent_type;
StoreType limit;
};

struct DecimalCastOperation {
Expand Down Expand Up @@ -635,7 +651,11 @@ struct DecimalCastOperation {
for (uint8_t i = state.decimal_count; i < state.scale; i++) {
state.result *= 10;
}
return true;
if (NEGATIVE) {
return state.result > -state.limit;
} else {
return state.result < state.limit;
}
}
};

Expand All @@ -658,6 +678,7 @@ bool TryDecimalStringCast(const char *string_ptr, idx_t string_size, T &result,
state.exponent_type = ExponentType::NONE;
state.round_set = false;
state.should_round = false;
state.limit = UnsafeNumericCast<T>(DecimalCastTraits<T>::POWERS_OF_TEN_CLASS::POWERS_OF_TEN[width]);
if (!TryIntegerCast<DecimalCastData<T>, true, true, DecimalCastOperation, false, decimal_separator>(
string_ptr, string_size, state, false)) {
string_t value(string_ptr, (uint32_t)string_size);
Expand All @@ -682,6 +703,7 @@ bool TryDecimalStringCast(const char *string_ptr, idx_t string_size, T &result,
state.exponent_type = ExponentType::NONE;
state.round_set = false;
state.should_round = false;
state.limit = UnsafeNumericCast<T>(DecimalCastTraits<T>::POWERS_OF_TEN_CLASS::POWERS_OF_TEN[width]);
if (!TryIntegerCast<DecimalCastData<T>, true, true, DecimalCastOperation, false, decimal_separator>(
string_ptr, string_size, state, false)) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions test/sql/types/decimal/test_decimal_from_string.test
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@ endloop
endloop

endloop

statement error
select cast('9.99' as decimal(1,0));
----
Conversion Error: Could not convert string

0 comments on commit 3aadb51

Please sign in to comment.