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