8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 12:43:03 +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); } 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. // 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)) if (cb->transliterate(from, to, charset2))
return; return;
ULONG len;
{ // scope { // scope
USHORT strtype_unused; USHORT strtype_unused;
UCHAR *ptr; 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; q = ptr;
} // end scope } // end scope
const USHORT to_size = TEXT_LEN(to); const USHORT to_size = TEXT_LEN(to);
UCHAR fill_char = ASCII_SPACE;
Jrd::CharSet* toCharset = cb->getToCharset(charset2); 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); cb->validateData(toCharset, length, q);
ULONG toLength = cb->validateLength(toCharset, length, q, to_size); ULONG toLength = cb->validateLength(toCharset, charset2, length, q, to_size);
len -= toLength;
ULONG fill = ULONG(to->dsc_length) - toLength;
if (charset2 == ttype_binary)
fill_char = 0x00;
switch (to->dsc_dtype) switch (to->dsc_dtype)
{ {
case dtype_text: case dtype_text:
CVT_COPY_BUFF(q, p, toLength);
if (fill > 0)
{ {
memset(p, fill_char, fill); ULONG fill = ULONG(to->dsc_length) - toLength;
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);
CVT_COPY_BUFF(q, p, 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) if (fill > 0)
{
// 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)
{ {
cb->err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << fb_assert(!toCharset || toCharset->getSpaceLength() == 1);
Arg::Gds(isc_trunc_limits) << UCHAR fillChar = toCharset ?
Arg::Num(to->getStringLength()) << Arg::Num(from->getStringLength())); *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; return;
@ -3635,8 +3596,8 @@ namespace
virtual CHARSET_ID getChid(const dsc* d); virtual CHARSET_ID getChid(const dsc* d);
virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2); virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2);
virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q); virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q);
virtual ULONG validateLength(Jrd::CharSet* toCharset, ULONG toLength, const UCHAR* start, virtual ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size); const USHORT size);
virtual SLONG getLocalDate(); virtual SLONG getLocalDate();
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp(); virtual ISC_TIMESTAMP getCurrentGmtTimeStamp();
virtual USHORT getSessionTimeZone(); 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) CHARSET_ID CommonCallbacks::getChid(const dsc* d)

View File

@ -58,8 +58,8 @@ public:
virtual CHARSET_ID getChid(const dsc* d) = 0; virtual CHARSET_ID getChid(const dsc* d) = 0;
virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2) = 0; virtual Jrd::CharSet* getToCharset(CHARSET_ID charset2) = 0;
virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q) = 0; virtual void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q) = 0;
virtual ULONG validateLength(Jrd::CharSet* toCharset, ULONG toLength, const UCHAR* start, virtual ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size) = 0; const USHORT size) = 0;
virtual SLONG getLocalDate() = 0; virtual SLONG getLocalDate() = 0;
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp() = 0; virtual ISC_TIMESTAMP getCurrentGmtTimeStamp() = 0;
virtual USHORT getSessionTimeZone() = 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)); CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc));
EngineCallbacks::instance->validateData(charSet, len, p); 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()) 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, ULONG EngineCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size) 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 srcCharLength = charSet->length(length, start, true);
const ULONG destCharLength = (ULONG) to_size / toCharSet->maxBytesPerChar(); const ULONG destCharLength = (ULONG) size / charSet->maxBytesPerChar();
if (srcCharLength > destCharLength) if (srcCharLength > destCharLength)
{ {
const ULONG spaceByteLength = toCharSet->getSpaceLength(); const ULONG spaceByteLength = charSet->getSpaceLength();
const ULONG trimmedByteLength = toCharSet->removeTrailingSpaces(toLength, start); const ULONG trimmedByteLength = charSet->removeTrailingSpaces(length, start);
const ULONG trimmedCharLength = srcCharLength - (toLength - trimmedByteLength) / spaceByteLength; const ULONG trimmedCharLength = srcCharLength - (length - trimmedByteLength) / spaceByteLength;
if (trimmedCharLength <= destCharLength) if (trimmedCharLength <= destCharLength)
return trimmedByteLength + (destCharLength - trimmedCharLength) * spaceByteLength; 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, ULONG TruncateCallbacks::validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size) 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); const ULONG spaceByteLength = charSet->getSpaceLength();
if (src_len <= dest_len) const ULONG trimmedByteLength = charSet->removeTrailingSpaces(length, start);
break; const ULONG trimmedCharLength = srcCharLength - (length - trimmedByteLength) / spaceByteLength;
toLength -= (src_len - dest_len); // truncate if (trimmedCharLength <= destCharLength)
if (first) return trimmedByteLength + (destCharLength - trimmedCharLength) * spaceByteLength;
ERR_post_warning(Arg::Warning(isc_truncate_warn) << Arg::Warning(truncateReason)); 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_ID getChid(const dsc* d);
virtual CharSet* getToCharset(CHARSET_ID charset2); virtual CharSet* getToCharset(CHARSET_ID charset2);
virtual void validateData(CharSet* toCharset, SLONG length, const UCHAR* q); virtual void validateData(CharSet* toCharset, SLONG length, const UCHAR* q);
virtual ULONG validateLength(CharSet* toCharset, ULONG toLength, const UCHAR* start, virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size); const USHORT size);
virtual SLONG getLocalDate(); virtual SLONG getLocalDate();
virtual ISC_TIMESTAMP getCurrentGmtTimeStamp(); virtual ISC_TIMESTAMP getCurrentGmtTimeStamp();
virtual USHORT getSessionTimeZone(); virtual USHORT getSessionTimeZone();
@ -76,8 +76,8 @@ namespace Jrd
{ {
} }
virtual ULONG validateLength(CharSet* toCharset, ULONG toLength, const UCHAR* start, virtual ULONG validateLength(CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT to_size); const USHORT size);
private: private:
const ISC_STATUS truncateReason; 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, ULONG to_len = INTL_convert_bytes(tdbb, to_cs, vstr,
to_size, from_cs, from_ptr, from_len, cb->err); 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; toLength = to_len;
((vary*) to->dsc_address)->vary_length = 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)) if (!toCharSet->wellFormed(to_len, q))
cb->err(Arg::Gds(isc_malformed_string)); 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; toLength = to_len;
from_fill = from_len - to_len; from_fill = from_len - to_len;