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:
parent
b0b4b1947a
commit
0174bda65d
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user