8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 09:20:39 +01:00

Reworked fix for #7997: Unexpected results when comparing integer with string containing value out of range of that integer datatype

This commit is contained in:
AlexPeshkoff 2024-02-14 14:10:35 +03:00
parent 35ecc50863
commit 11920822e0
4 changed files with 214 additions and 218 deletions

View File

@ -59,7 +59,7 @@ namespace Firebird {
Int128 Int128::set(const char* value)
{
// This is simplified method - it does not perform all what's needed for CVT_decompose
// This is simplified method - it does not perform all what's needed for full conversion
for (v = 0; ; ++value)
{
if (*value < '0' or *value > '9')
@ -319,7 +319,7 @@ Int128 Int128::set(SINT64 value, int scale)
Int128 Int128::set(const char* value)
{
// This is simplified method - it does not perform all what's needed for CVT_decompose
// This is simplified method - it does not perform all what's needed for full conversion
v.FromString(value);
return *this;
}

View File

@ -145,6 +145,11 @@ static void localError(const Firebird::Arg::StatusVector&);
static SSHORT cvt_get_short(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err);
static void make_null_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction);
namespace {
class RetPtr;
}
static SSHORT cvt_decompose(const char*, USHORT, RetPtr* return_value, ErrorFunction err);
class DummyException {};
namespace
@ -277,6 +282,88 @@ static InitInstance<TimeZoneTrie> timeZoneTrie;
//#endif
namespace {
class RetPtr
{
public:
virtual ~RetPtr() { }
enum lb10 {RETVAL_OVERFLOW, RETVAL_POSSIBLE_OVERFLOW, RETVAL_NO_OVERFLOW};
virtual USHORT maxSize() = 0;
virtual void truncate8() = 0;
virtual void truncate16() = 0;
virtual lb10 compareLimitBy10() = 0;
virtual void nextDigit(unsigned digit, unsigned base) = 0;
virtual bool isLowerLimit() = 0;
virtual void neg() = 0;
};
template <class Traits>
class RetValue : public RetPtr
{
public:
RetValue(typename Traits::ValueType* ptr)
: return_value(ptr)
{
value = 0;
}
~RetValue()
{
*return_value = value;
}
USHORT maxSize() override
{
return sizeof(typename Traits::ValueType);
}
void truncate8() override
{
ULONG mask = 0xFFFFFFFF;
value &= mask;
}
void truncate16() override
{
FB_UINT64 mask = 0xFFFFFFFFFFFFFFFF;
value &= mask;
}
lb10 compareLimitBy10() override
{
if (static_cast<typename Traits::UnsignedType>(value) > Traits::UPPER_LIMIT_BY_10)
return RETVAL_OVERFLOW;
if (static_cast<typename Traits::UnsignedType>(value) == Traits::UPPER_LIMIT_BY_10)
return RETVAL_POSSIBLE_OVERFLOW;
return RETVAL_NO_OVERFLOW;
}
void nextDigit(unsigned digit, unsigned base) override
{
value *= base;
value += digit;
}
bool isLowerLimit() override
{
return value == Traits::LOWER_LIMIT;
}
void neg() override
{
value = -value;
}
protected:
typename Traits::ValueType value;
typename Traits::ValueType* return_value;
};
} // anonymous namespace
static const double eps_double = 1e-14;
static const double eps_float = 1e-5;
@ -2207,6 +2294,15 @@ void adjustForScale(V& val, SSHORT scale, const V limit, ErrorFunction err)
}
class SSHORTTraits
{
public:
typedef SSHORT ValueType;
typedef USHORT UnsignedType;
static const USHORT UPPER_LIMIT_BY_10 = MAX_SSHORT / 10;
static const SSHORT LOWER_LIMIT = MIN_SSHORT;
};
static SSHORT cvt_get_short(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
@ -2227,7 +2323,9 @@ static SSHORT cvt_get_short(const dsc* desc, SSHORT scale, DecimalStatus decSt,
VaryStr<20> buffer; // long enough to represent largest short in ASCII
const char* p;
USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err);
scale -= CVT_decompose(p, length, &value, err);
RetValue<SSHORTTraits> rv(&value);
scale -= cvt_decompose(p, length, &rv, err);
adjustForScale(value, scale, SHORT_LIMIT, err);
}
@ -2241,6 +2339,16 @@ static SSHORT cvt_get_short(const dsc* desc, SSHORT scale, DecimalStatus decSt,
return value;
}
class SLONGTraits
{
public:
typedef SLONG ValueType;
typedef ULONG UnsignedType;
static const ULONG UPPER_LIMIT_BY_10 = MAX_SLONG / 10;
static const SLONG LOWER_LIMIT = MIN_SLONG;
};
SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
@ -2354,7 +2462,9 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc
{
USHORT length =
CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err);
scale -= CVT_decompose(p, length, &value, err);
RetValue<SLONGTraits> rv(&value);
scale -= cvt_decompose(p, length, &rv, err);
}
break;
@ -3623,29 +3733,12 @@ double CVT_power_of_ten(const int scale)
}
class RetPtr
{
public:
virtual ~RetPtr() { }
enum lb10 {RETVAL_OVERFLOW, RETVAL_POSSIBLE_OVERFLOW, RETVAL_NO_OVERFLOW};
virtual USHORT maxSize() = 0;
virtual void truncate8() = 0;
virtual void truncate16() = 0;
virtual lb10 compareLimitBy10() = 0;
virtual void nextDigit(unsigned digit, unsigned base) = 0;
virtual bool isLowerLimit() = 0;
virtual void neg() = 0;
};
static void hex_to_value(const char*& string, const char* end, RetPtr* retValue);
static SSHORT cvt_decompose(const char* string,
USHORT length,
RetPtr* return_value,
ErrorFunction err,
int* overflow = nullptr)
ErrorFunction err)
{
/**************************************
*
@ -3743,23 +3836,15 @@ static SSHORT cvt_decompose(const char* string,
if (p >= end)
continue;
}
if (overflow)
*overflow = 1;
else
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range));
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range));
return 0;
case RetPtr::RETVAL_POSSIBLE_OVERFLOW:
if ((*p > '8' && sign == -1) || (*p > '7' && sign != -1))
{
if (overflow)
*overflow = 1;
else
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range));
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range));
return 0;
}
break;
default:
break;
}
@ -3875,153 +3960,6 @@ static SSHORT cvt_decompose(const char* string,
}
template <class Traits>
class RetValue : public RetPtr
{
public:
RetValue(typename Traits::ValueType* ptr)
: return_value(ptr)
{
value = 0;
}
~RetValue()
{
*return_value = value;
}
USHORT maxSize()
{
return sizeof(typename Traits::ValueType);
}
void truncate8()
{
ULONG mask = 0xFFFFFFFF;
value &= mask;
}
void truncate16()
{
FB_UINT64 mask = 0xFFFFFFFFFFFFFFFF;
value &= mask;
}
lb10 compareLimitBy10()
{
if (static_cast<typename Traits::UnsignedType>(value) > Traits::UPPER_LIMIT_BY_10)
return RETVAL_OVERFLOW;
if (static_cast<typename Traits::UnsignedType>(value) == Traits::UPPER_LIMIT_BY_10)
return RETVAL_POSSIBLE_OVERFLOW;
return RETVAL_NO_OVERFLOW;
}
void nextDigit(unsigned digit, unsigned base)
{
value *= base;
value += digit;
}
bool isLowerLimit()
{
return value == Traits::LOWER_LIMIT;
}
void neg()
{
value = -value;
}
private:
typename Traits::ValueType value;
typename Traits::ValueType* return_value;
};
class SSHORTTraits
{
public:
typedef SSHORT ValueType;
typedef USHORT UnsignedType;
static const USHORT UPPER_LIMIT_BY_10 = MAX_SSHORT / 10;
static const SSHORT LOWER_LIMIT = MIN_SSHORT;
};
SSHORT CVT_decompose(const char* str, USHORT len, SSHORT* val, ErrorFunction err)
{
/**************************************
*
* d e c o m p o s e
*
**************************************
*
* Functional description
* Decompose a numeric string in mantissa and exponent,
* or if it is in hexadecimal notation.
*
**************************************/
RetValue<SSHORTTraits> value(val);
return cvt_decompose(str, len, &value, err);
}
class SLONGTraits
{
public:
typedef SLONG ValueType;
typedef ULONG UnsignedType;
static const ULONG UPPER_LIMIT_BY_10 = MAX_SLONG / 10;
static const SLONG LOWER_LIMIT = MIN_SLONG;
};
SSHORT CVT_decompose(const char* str, USHORT len, SLONG* val, ErrorFunction err)
{
/**************************************
*
* d e c o m p o s e
*
**************************************
*
* Functional description
* Decompose a numeric string in mantissa and exponent,
* or if it is in hexadecimal notation.
*
**************************************/
RetValue<SLONGTraits> value(val);
return cvt_decompose(str, len, &value, err);
}
class SINT64Traits
{
public:
typedef SINT64 ValueType;
typedef FB_UINT64 UnsignedType;
static const FB_UINT64 UPPER_LIMIT_BY_10 = MAX_SINT64 / 10;
static const SINT64 LOWER_LIMIT = MIN_SINT64;
};
SSHORT CVT_decompose(const char* str, USHORT len, SINT64* val, ErrorFunction err, int* overflow)
{
/**************************************
*
* d e c o m p o s e
*
**************************************
*
* Functional description
* Decompose a numeric string in mantissa and exponent,
* or if it is in hexadecimal notation.
*
**************************************/
RetValue<SINT64Traits> value(val);
return cvt_decompose(str, len, &value, err, overflow);
}
class I128Traits
{
public:
@ -4034,6 +3972,25 @@ public:
const CInt128 I128Traits::UPPER_LIMIT_BY_10(CInt128(CInt128::MkMax) / 10);
const CInt128 I128Traits::LOWER_LIMIT(CInt128::MkMin);
class RetI128 : public RetValue<I128Traits>
{
public:
RetI128(Int128* v)
: RetValue<I128Traits>(v)
{ }
lb10 compareLimitBy10() override
{
lb10 rc = RetValue<I128Traits>::compareLimitBy10();
if (rc != RETVAL_NO_OVERFLOW)
return rc;
if (value.sign() < 0)
return RETVAL_OVERFLOW;
return RETVAL_NO_OVERFLOW;
}
};
SSHORT CVT_decompose(const char* str, USHORT len, Int128* val, ErrorFunction err)
{
/**************************************
@ -4049,7 +4006,7 @@ SSHORT CVT_decompose(const char* str, USHORT len, Int128* val, ErrorFunction err
**************************************/
RetValue<I128Traits> value(val);
RetI128 value(val);
return cvt_decompose(str, len, &value, err);
}
@ -4517,6 +4474,15 @@ const UCHAR* CVT_get_bytes(const dsc* desc, unsigned& size)
}
class SINT64Traits
{
public:
typedef SINT64 ValueType;
typedef FB_UINT64 UnsignedType;
static const FB_UINT64 UPPER_LIMIT_BY_10 = MAX_SINT64 / 10;
static const SINT64 LOWER_LIMIT = MIN_SINT64;
};
SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
@ -4572,8 +4538,10 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc
{
USHORT length =
CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err);
SINT64 i64;
scale -= CVT_decompose(p, length, &i64, err);
RetValue<SINT64Traits> rv(&i64);
scale -= cvt_decompose(p, length, &rv, err);
SINT64_to_SQUAD(i64, value);
}
break;
@ -4612,7 +4580,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunc
}
SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err, int* overflow)
SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err)
{
/**************************************
*
@ -4714,7 +4682,9 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFu
{
USHORT length =
CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err);
scale -= CVT_decompose(p, length, &value, err, overflow);
RetValue<SINT64Traits> rv(&value);
scale -= cvt_decompose(p, length, &rv, err);
}
break;

View File

@ -97,13 +97,10 @@ Firebird::Int128 CVT_hex_to_int128(const char* str, USHORT len);
USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*);
void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction);
SSHORT CVT_decompose(const char*, USHORT, SSHORT*, ErrorFunction);
SSHORT CVT_decompose(const char*, USHORT, SLONG*, ErrorFunction);
SSHORT CVT_decompose(const char*, USHORT, SINT64*, ErrorFunction, int* overflow = nullptr);
SSHORT CVT_decompose(const char*, USHORT, Firebird::Int128*, ErrorFunction);
USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction);
USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*);
SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction, int* overflow = nullptr);
SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME,
bool, Firebird::Callbacks*);

View File

@ -101,9 +101,9 @@ const BYTE CVT2_compare_priority[] =
10, // dtype_int64 - goes right after long
25, // dtype_dbkey - compares with nothing except itself
26, // dtype_boolean - compares with nothing except itself
12, // dtype_int128 - go after quad
16, // dec64 - go after dtype_d_float
17, // dec128 - go after dec64 and before dtype_sql_date
12, // dtype_int128 - go after quad
20, // dtype_sql_time_tz - go after dtype_sql_time
22, // dtype_timestamp_tz - go after dtype_timestamp
99, // dtype_ex_time_tz - should not be used here
@ -203,6 +203,39 @@ bool CVT2_get_binary_comparable_desc(dsc* result, const dsc* arg1, const dsc* ar
}
static int cmp_numeric_string(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt)
{
/**************************************
*
* c m p _ n u m e r i c _ s t r i n g
*
**************************************
*
* Functional description
* Compare any numeric value with string. Return (-1, 0, 1) if a<b, a=b, or a>b.
*
**************************************/
fb_assert(arg1->isNumeric());
fb_assert(arg2->isText());
Decimal128 buffer; // enough to fit any required data
SSHORT scale = 0;
UCHAR* text = arg2->dsc_address;
if (arg2->dsc_dtype == dtype_varying)
text += sizeof(USHORT);
dsc num2;
num2.dsc_dtype = CVT_get_numeric(text, TEXT_LEN(arg2), &scale, &buffer);
num2.dsc_address = (UCHAR*)&buffer;
num2.dsc_scale = scale;
num2.dsc_length = type_lengths[num2.dsc_dtype];
num2.dsc_sub_type = 0;
num2.dsc_flags = 0;
return CVT2_compare(arg1, &num2, decSt);
}
int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt)
{
/**************************************
@ -520,13 +553,13 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_short:
{
SSHORT scale;
if (arg2->dsc_dtype > dtype_varying)
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
else
scale = arg1->dsc_scale;
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
const SLONG temp1 = CVT_get_long(arg1, scale, decSt, ERR_post);
const SLONG temp2 = CVT_get_long(arg2, scale, decSt, ERR_post);
if (temp1 == temp2)
return 0;
if (temp1 > temp2)
@ -538,22 +571,12 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
// Since longs may overflow when scaled, use int64 instead
case dtype_int64:
{
SSHORT scale;
if (arg2->dsc_dtype > dtype_varying)
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
else
scale = arg1->dsc_scale;
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
int overflow = 0;
const SINT64 temp1 = CVT_get_int64(arg1, scale, decSt, ERR_post, &overflow);
const SINT64 temp2 = CVT_get_int64(arg2, scale, decSt, ERR_post, &overflow);
if (overflow)
{
const Int128 temp1 = CVT_get_int128(arg1, scale, decSt, ERR_post);
const Int128 temp2 = CVT_get_int128(arg2, scale, decSt, ERR_post);
return temp1.compare(temp2);
}
SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
const SINT64 temp1 = CVT_get_int64(arg1, scale, decSt, ERR_post);
const SINT64 temp2 = CVT_get_int64(arg2, scale, decSt, ERR_post);
if (temp1 == temp2)
return 0;
@ -564,11 +587,10 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_quad:
{
SSHORT scale;
if (arg2->dsc_dtype > dtype_varying)
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
else
scale = arg1->dsc_scale;
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
const SQUAD temp1 = CVT_get_quad(arg1, scale, decSt, ERR_post);
const SQUAD temp2 = CVT_get_quad(arg2, scale, decSt, ERR_post);
return QUAD_COMPARE(&temp1, &temp2);
@ -576,6 +598,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_real:
{
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
const float temp1 = (float) CVT_get_double(arg1, decSt, ERR_post);
const float temp2 = (float) CVT_get_double(arg2, decSt, ERR_post);
if (temp1 == temp2)
@ -587,6 +612,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_double:
{
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
const double temp1 = CVT_get_double(arg1, decSt, ERR_post);
const double temp2 = CVT_get_double(arg2, decSt, ERR_post);
if (temp1 == temp2)
@ -598,6 +626,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_dec64:
{
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
const Decimal64 temp1 = CVT_get_dec64(arg1, decSt, ERR_post);
const Decimal64 temp2 = CVT_get_dec64(arg2, decSt, ERR_post);
return temp1.compare(decSt, temp2);
@ -612,12 +643,10 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
case dtype_int128:
{
SSHORT scale;
if (arg2->dsc_dtype > dtype_varying)
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
else
scale = arg1->dsc_scale;
if (arg2->isText())
return cmp_numeric_string(arg1, arg2, decSt);
SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
const Int128 temp1 = CVT_get_int128(arg1, scale, decSt, ERR_post);
const Int128 temp2 = CVT_get_int128(arg2, scale, decSt, ERR_post);
return temp1.compare(temp2);