8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 06:43:04 +01:00

Fixed CORE-6414, CORE-6415 and CORE-6419.

CORE-6414: Error message "expected length N, actual M" contains wrong value of M when charset UTF8 is used in the field declaration of a table.
CORE-6415: Error message "malformed string' is issued instead of "expected: N, actual : M" when UTF-8 charset is used and default value for declared column is greater than this column length.
CORE-6419: Truncation of strings to put in MON$ tables do not work correctly.
This commit is contained in:
Adriano dos Santos Fernandes 2020-10-17 22:55:59 -03:00
parent b0b4b1947a
commit 0174bda65d
6 changed files with 105 additions and 106 deletions

View File

@ -474,7 +474,7 @@ static void integer_to_text(const dsc* from, dsc* to, Callbacks* cb)
} while (++scale);
}
length = cb->validateLength(cb->getToCharset(to->getCharSet()), length, start, TEXT_LEN(to));
length = cb->validateLength(cb->getToCharset(to->getCharSet()), to->getCharSet(), length, start, TEXT_LEN(to));
// If padding is required, do it now.
@ -1853,95 +1853,56 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
if (cb->transliterate(from, to, charset2))
return;
ULONG len;
{ // scope
USHORT strtype_unused;
UCHAR *ptr;
length = len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb);
length = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb);
q = ptr;
} // end scope
const USHORT to_size = TEXT_LEN(to);
UCHAR fill_char = ASCII_SPACE;
Jrd::CharSet* toCharset = cb->getToCharset(charset2);
switch (to->dsc_dtype)
{
case dtype_text:
length = MIN(length, to->dsc_length);
break;
case dtype_cstring:
// Note: Following is only correct for narrow and
// multibyte character sets which use a zero
// byte to represent end-of-string
fb_assert(to->dsc_length > 0);
length = MIN(length, ULONG(to->dsc_length - 1));
break;
case dtype_varying:
length = to->dsc_length > sizeof(USHORT) ?
MIN(length, (ULONG(to->dsc_length) - sizeof(USHORT))) : 0;
break;
}
cb->validateData(toCharset, length, q);
ULONG toLength = cb->validateLength(toCharset, length, q, to_size);
len -= toLength;
ULONG fill = ULONG(to->dsc_length) - toLength;
if (charset2 == ttype_binary)
fill_char = 0x00;
ULONG toLength = cb->validateLength(toCharset, charset2, length, q, to_size);
switch (to->dsc_dtype)
{
case dtype_text:
CVT_COPY_BUFF(q, p, toLength);
if (fill > 0)
case dtype_text:
{
memset(p, fill_char, fill);
p += fill;
// Note: above is correct only for narrow
// and multi-byte character sets which
// use ASCII for the SPACE character.
}
break;
case dtype_cstring:
CVT_COPY_BUFF(q, p, toLength);
*p = 0;
break;
case dtype_varying:
if (to->dsc_length > sizeof(USHORT))
{
// TMN: Here we should really have the following fb_assert
// fb_assert(length <= MAX_USHORT);
((vary*) p)->vary_length = (USHORT) toLength;
p = reinterpret_cast<UCHAR*>(((vary*) p)->vary_string);
ULONG fill = ULONG(to->dsc_length) - toLength;
CVT_COPY_BUFF(q, p, toLength);
}
else
memset(to->dsc_address, 0, to->dsc_length); // the best we can do
break;
}
if (len && toLength == length)
{
// Scan the truncated string to ensure only spaces lost
// Warning: it is correct only for narrow and multi-byte
// character sets which use ASCII or NULL for the SPACE character
do {
if (*q++ != fill_char && toLength == length)
if (fill > 0)
{
cb->err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) <<
Arg::Gds(isc_trunc_limits) <<
Arg::Num(to->getStringLength()) << Arg::Num(from->getStringLength()));
fb_assert(!toCharset || toCharset->getSpaceLength() == 1);
UCHAR fillChar = toCharset ?
*toCharset->getSpace() :
(charset2 == ttype_binary ? 0x00 : ASCII_SPACE);
memset(p, fillChar, fill);
p += fill;
}
} while (--len);
break;
}
case dtype_cstring:
CVT_COPY_BUFF(q, p, toLength);
*p = 0;
break;
case dtype_varying:
if (to->dsc_length > sizeof(USHORT))
{
// TMN: Here we should really have the following fb_assert
// fb_assert(length <= MAX_USHORT);
((vary*) p)->vary_length = (USHORT) toLength;
p = reinterpret_cast<UCHAR*>(((vary*) p)->vary_string);
CVT_COPY_BUFF(q, p, toLength);
}
else
memset(to->dsc_address, 0, to->dsc_length); // the best we can do
break;
}
}
return;
@ -3635,8 +3596,8 @@ namespace
virtual CHARSET_ID getChid(const dsc* d);
virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2);
virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q);
virtual ULONG validateLength(Jrd::CharSet* toCharset, ULONG toLength, const UCHAR* start,
const USHORT to_size);
virtual ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size);
virtual SLONG getLocalDate();
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp();
virtual USHORT getSessionTimeZone();
@ -3658,9 +3619,32 @@ namespace
{
}
ULONG CommonCallbacks::validateLength(Jrd::CharSet*, ULONG l, const UCHAR*, const USHORT)
ULONG CommonCallbacks::validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size)
{
return l;
if (length > size)
{
fb_assert(!charSet || (!charSet->isMultiByte() && charSet->getSpaceLength() == 1));
UCHAR fillChar = charSet ?
*charSet->getSpace() :
(charSetId == ttype_binary ? 0x00 : ASCII_SPACE);
const UCHAR* p = start + size;
// Scan the truncated string to ensure only spaces lost
while (p < start + length)
{
if (*p++ != fillChar)
{
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) <<
Arg::Gds(isc_trunc_limits) <<
Arg::Num(size) << Arg::Num(length));
}
}
}
return MIN(length, size);
}
CHARSET_ID CommonCallbacks::getChid(const dsc* d)

View File

@ -58,8 +58,8 @@ public:
virtual CHARSET_ID getChid(const dsc* d) = 0;
virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2) = 0;
virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q) = 0;
virtual ULONG validateLength(Jrd::CharSet* toCharset, ULONG toLength, const UCHAR* start,
const USHORT to_size) = 0;
virtual ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size) = 0;
virtual SLONG getLocalDate() = 0;
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp() = 0;
virtual USHORT getSessionTimeZone() = 0;

View File

@ -9749,7 +9749,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const
CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc));
EngineCallbacks::instance->validateData(charSet, len, p);
EngineCallbacks::instance->validateLength(charSet, len, p, maxLen);
EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(desc), len, p, maxLen);
}
else if (desc->isBlob())
{

View File

@ -496,19 +496,21 @@ void EngineCallbacks::validateData(CharSet* toCharSet, SLONG length, const UCHAR
}
ULONG EngineCallbacks::validateLength(CharSet* toCharSet, ULONG toLength, const UCHAR* start,
const USHORT to_size)
ULONG EngineCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size)
{
if (toCharSet && toCharSet->isMultiByte())
fb_assert(charSet);
if (charSet && (charSet->isMultiByte() || length > size))
{
const ULONG srcCharLength = toCharSet->length(toLength, start, true);
const ULONG destCharLength = (ULONG) to_size / toCharSet->maxBytesPerChar();
const ULONG srcCharLength = charSet->length(length, start, true);
const ULONG destCharLength = (ULONG) size / charSet->maxBytesPerChar();
if (srcCharLength > destCharLength)
{
const ULONG spaceByteLength = toCharSet->getSpaceLength();
const ULONG trimmedByteLength = toCharSet->removeTrailingSpaces(toLength, start);
const ULONG trimmedCharLength = srcCharLength - (toLength - trimmedByteLength) / spaceByteLength;
const ULONG spaceByteLength = charSet->getSpaceLength();
const ULONG trimmedByteLength = charSet->removeTrailingSpaces(length, start);
const ULONG trimmedCharLength = srcCharLength - (length - trimmedByteLength) / spaceByteLength;
if (trimmedCharLength <= destCharLength)
return trimmedByteLength + (destCharLength - trimmedCharLength) * spaceByteLength;
@ -520,30 +522,43 @@ ULONG EngineCallbacks::validateLength(CharSet* toCharSet, ULONG toLength, const
}
}
return toLength;
return length;
}
ULONG TruncateCallbacks::validateLength(CharSet* toCharSet, ULONG toLength, const UCHAR* start,
const USHORT to_size)
ULONG TruncateCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size)
{
if (toCharSet && toCharSet->isMultiByte())
fb_assert(charSet);
if (charSet && (charSet->isMultiByte() || length > size))
{
const ULONG dest_len = (ULONG) to_size / toCharSet->maxBytesPerChar();
const ULONG srcCharLength = charSet->length(length, start, true);
const ULONG destCharLength = (ULONG) size / charSet->maxBytesPerChar();
for (bool first = true; ; first = false)
if (srcCharLength > destCharLength)
{
const ULONG src_len = toCharSet->length(toLength, start, false);
if (src_len <= dest_len)
break;
const ULONG spaceByteLength = charSet->getSpaceLength();
const ULONG trimmedByteLength = charSet->removeTrailingSpaces(length, start);
const ULONG trimmedCharLength = srcCharLength - (length - trimmedByteLength) / spaceByteLength;
toLength -= (src_len - dest_len); // truncate
if (first)
ERR_post_warning(Arg::Warning(isc_truncate_warn) << Arg::Warning(truncateReason));
if (trimmedCharLength <= destCharLength)
return trimmedByteLength + (destCharLength - trimmedCharLength) * spaceByteLength;
else if (charSet->isMultiByte())
{
HalfStaticArray<UCHAR, BUFFER_SMALL, USHORT> buffer(size);
length = charSet->substring(
length, start, buffer.getCapacity(), buffer.begin(),
0, destCharLength);
}
else
length = size;
ERR_post_warning(Arg::Warning(isc_truncate_warn) << Arg::Warning(truncateReason));
}
}
return toLength;
return length;
}

View File

@ -57,8 +57,8 @@ namespace Jrd
virtual CHARSET_ID getChid(const dsc* d);
virtual CharSet* getToCharset(CHARSET_ID charset2);
virtual void validateData(CharSet* toCharset, SLONG length, const UCHAR* q);
virtual ULONG validateLength(CharSet* toCharset, ULONG toLength, const UCHAR* start,
const USHORT to_size);
virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size);
virtual SLONG getLocalDate();
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp();
virtual USHORT getSessionTimeZone();
@ -76,8 +76,8 @@ namespace Jrd
{
}
virtual ULONG validateLength(CharSet* toCharset, ULONG toLength, const UCHAR* start,
const USHORT to_size);
virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size);
private:
const ISC_STATUS truncateReason;

View File

@ -901,7 +901,7 @@ int INTL_convert_string(dsc* to, const dsc* from, Firebird::Callbacks* cb)
ULONG to_len = INTL_convert_bytes(tdbb, to_cs, vstr,
to_size, from_cs, from_ptr, from_len, cb->err);
to_len = cb->validateLength(toCharSet, to_len, vstr, to_size);
to_len = cb->validateLength(toCharSet, to_cs, to_len, vstr, to_size);
toLength = to_len;
((vary*) to->dsc_address)->vary_length = to_len;
@ -914,7 +914,7 @@ int INTL_convert_string(dsc* to, const dsc* from, Firebird::Callbacks* cb)
if (!toCharSet->wellFormed(to_len, q))
cb->err(Arg::Gds(isc_malformed_string));
to_len = cb->validateLength(toCharSet, to_len, q, to_size);
to_len = cb->validateLength(toCharSet, to_cs, to_len, q, to_size);
toLength = to_len;
from_fill = from_len - to_len;