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

1) Make SUBSTRING of BLOB work

2) More refactoration on describing result of expressions (concatenate and substring)
This commit is contained in:
asfernandes 2006-08-12 03:17:01 +00:00
parent e5e678f587
commit 4180a46169
9 changed files with 316 additions and 267 deletions

View File

@ -444,91 +444,35 @@ void MAKE_desc(dsql_req* request, dsc* desc, dsql_nod* node, dsql_nod* null_repl
return;
case nod_concatenate:
{
MAKE_desc(request, &desc1, node->nod_arg[0], node->nod_arg[1]);
MAKE_desc(request, &desc2, node->nod_arg[1], node->nod_arg[0]);
if (node->nod_arg[0]->nod_type == nod_null &&
node->nod_arg[1]->nod_type == nod_null)
{
// NULL || NULL = NULL of VARCHAR(1) CHARACTER SET NONE
desc->dsc_dtype = dtype_varying;
desc->dsc_scale = 0;
desc->dsc_ttype() = 0;
desc->dsc_length = sizeof(USHORT) + 1;
desc->dsc_flags |= DSC_nullable;
return;
}
Jrd::DataTypeUtil::makeConcatenate(desc, &desc1, &desc2);
if (desc->dsc_dtype == dtype_varying)
{
ULONG length =
((node->nod_arg[0]->nod_type == nod_null) ? 0 : DSC_string_length(&desc1)) /
METD_get_charset_bpc(request, INTL_GET_CHARSET(&desc1));
length +=
((node->nod_arg[1]->nod_type == nod_null) ? 0 : DSC_string_length(&desc2)) /
METD_get_charset_bpc(request, INTL_GET_CHARSET(&desc2));
desc->dsc_length = UTLD_char_length_to_byte_length(
length, METD_get_charset_bpc(request, INTL_GET_CHARSET(desc))) + sizeof(USHORT);
}
desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable;
}
MAKE_desc(request, &desc1, node->nod_arg[0], node->nod_arg[1]);
MAKE_desc(request, &desc2, node->nod_arg[1], node->nod_arg[0]);
DSqlDataTypeUtil(request).makeConcatenate(desc, &desc1, &desc2);
return;
case nod_derived_field:
MAKE_desc(request, desc, node->nod_arg[e_derived_field_value], null_replacement);
return;
case nod_upcase:
case nod_lowcase:
case nod_substr:
MAKE_desc(request, &desc1, node->nod_arg[0], null_replacement);
if (desc1.dsc_dtype <= dtype_any_text ||
(node->nod_type != nod_substr && desc1.dsc_dtype == dtype_blob))
{
*desc = desc1;
return;
}
desc->dsc_dtype = dtype_varying;
desc->dsc_scale = 0;
case nod_upcase:
case nod_lowcase:
MAKE_desc(request, &desc1, node->nod_arg[0], null_replacement);
if (desc1.dsc_dtype <= dtype_any_text || desc1.dsc_dtype == dtype_blob)
{
*desc = desc1;
return;
}
/* Beware that JRD treats substring() always as returning CHAR
instead of VARCHAR for historical reasons. */
if (node->nod_type == nod_substr && desc1.dsc_dtype == dtype_blob)
{
dsql_nod* for_node = node->nod_arg[e_substr_length];
// Migrate the charset and collation from the blob to the string.
desc->dsc_ttype() = desc1.dsc_blob_ttype();
USHORT maxBytesPerChar = METD_get_charset_bpc(request, desc1.dsc_scale); // BLOB character set
// Set maximum possible length
SLONG length = UTLD_char_length_to_byte_length(MAX_COLUMN_SIZE - sizeof(USHORT), maxBytesPerChar);
if (for_node->nod_type == nod_constant &&
for_node->nod_desc.dsc_dtype == dtype_long)
{
// We have a constant passed as length, so
// use the real length
length = UTLD_char_length_to_byte_length(*(SLONG *) for_node->nod_desc.dsc_address, maxBytesPerChar);
if (length < 0 || length > MAX_COLUMN_SIZE - sizeof(USHORT))
length = UTLD_char_length_to_byte_length(MAX_COLUMN_SIZE - sizeof(USHORT), maxBytesPerChar);
}
desc->dsc_length = sizeof(USHORT) + length;
}
else
{
desc->dsc_ttype() = ttype_ascii;
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
}
desc->dsc_dtype = dtype_varying;
desc->dsc_scale = 0;
desc->dsc_ttype() = ttype_ascii;
desc->dsc_length = sizeof(USHORT) + DSC_string_length(&desc1);
desc->dsc_flags = desc1.dsc_flags & DSC_nullable;
return;
case nod_substr:
MAKE_desc(request, &desc1, node->nod_arg[0], null_replacement);
DSqlDataTypeUtil(request).makeSubstr(desc, &desc1);
return;
case nod_trim:
MAKE_desc(request, &desc1, node->nod_arg[e_trim_value], null_replacement);

View File

@ -41,6 +41,10 @@
#include "../dsql/utld_proto.h"
#include "../jrd/gds_proto.h"
#if !defined(REQUESTER) && !defined(SUPERCLIENT)
#include "../dsql/metd_proto.h"
#endif
static void cleanup(void *);
static ISC_STATUS error_dsql_804(ISC_STATUS *, ISC_STATUS);
@ -1044,3 +1048,12 @@ static void xsqlvar_to_sqlvar( XSQLVAR * xsqlvar, SQLVAR * sqlvar)
sqlvar->sqllen = sizeof(ISC_QUAD) | (xsqlvar->sqlscale << 8);
}
#if !defined(REQUESTER) && !defined(SUPERCLIENT)
UCHAR DSqlDataTypeUtil::maxBytesPerChar(UCHAR charSet)
{
return METD_get_charset_bpc(request, charSet);
}
#endif

View File

@ -27,6 +27,10 @@
#ifndef DSQL_UTLD_PROTO_H
#define DSQL_UTLD_PROTO_H
#if !defined(REQUESTER) && !defined(SUPERCLIENT)
#include "../jrd/DataTypeUtil.h"
#endif
struct sqlda_sup;
USHORT UTLD_char_length_to_byte_length(USHORT lengthInChars, USHORT maxBytesPerChar);
@ -37,5 +41,28 @@ ISC_STATUS UTLD_parse_sqlda(ISC_STATUS*, sqlda_sup* const, USHORT*, USHORT*,
void UTLD_save_status_strings(ISC_STATUS*);
SCHAR* UTLD_skip_sql_info(SCHAR*);
#if !defined(REQUESTER) && !defined(SUPERCLIENT)
class dsql_req;
class DSqlDataTypeUtil : public DataTypeUtilBase
{
public:
DSqlDataTypeUtil(dsql_req* request)
: request(request)
{
}
public:
virtual UCHAR maxBytesPerChar(UCHAR charSet);
private:
dsql_req* request;
};
#endif
#endif // DSQL_UTLD_PROTO_H

View File

@ -30,11 +30,11 @@
#include "../jrd/dsc.h"
#include "../jrd/ibase.h"
#include "../jrd/intl.h"
namespace Jrd {
#include "../jrd/intl_proto.h"
#include "../jrd/gdsassert.h"
SSHORT DataTypeUtil::getResultBlobSubType(const dsc* value1, const dsc* value2)
SSHORT DataTypeUtilBase::getResultBlobSubType(const dsc* value1, const dsc* value2)
{
SSHORT subType1 = value1->getBlobSubType();
SSHORT subType2 = value2->getBlobSubType();
@ -46,7 +46,7 @@ SSHORT DataTypeUtil::getResultBlobSubType(const dsc* value1, const dsc* value2)
}
USHORT DataTypeUtil::getResultTextType(const dsc* value1, const dsc* value2)
USHORT DataTypeUtilBase::getResultTextType(const dsc* value1, const dsc* value2)
{
USHORT cs1 = value1->getCharSet();
USHORT cs2 = value2->getCharSet();
@ -63,27 +63,109 @@ USHORT DataTypeUtil::getResultTextType(const dsc* value1, const dsc* value2)
}
// ASF: We don't calculate string lengths because we may be called
// from the engine or DSQL, and charset max-bytes-per-char handling
// is different (TextType->maxBytesPerChar() / METD_get_charset_bpc).
// When DSQL is in the engine, we could calculate lengths directly
// here instead of in jrd/cmp.cpp and dsql/make.cpp.
void DataTypeUtil::makeConcatenate(dsc* result, const dsc* value1, const dsc* value2)
void DataTypeUtilBase::clearDsc(dsc* desc)
{
memset(result, 0, sizeof(*result));
memset(desc, 0, sizeof(*desc));
}
if (value1->isBlob() || value2->isBlob())
ULONG DataTypeUtilBase::convertLength(const dsc* src, const dsc* dst)
{
fb_assert(dst->isText());
UCHAR dstCharSet = dst->getCharSet();
if (dstCharSet == CS_NONE || dstCharSet == CS_BINARY)
return src->getStringLength();
else
{
return (src->getStringLength() / maxBytesPerChar(src->getCharSet())) *
maxBytesPerChar(dstCharSet);
}
}
ULONG DataTypeUtilBase::fixLength(const dsc* desc, ULONG length)
{
UCHAR bpc = maxBytesPerChar(desc->getCharSet());
return MIN(((MAX_COLUMN_SIZE - sizeof(USHORT)) / bpc) * bpc, length);
}
void DataTypeUtilBase::makeConcatenate(dsc* result, const dsc* value1, const dsc* value2)
{
clearDsc(result);
if (value1->isNull() && value2->isNull())
{
makeNullString(result);
return;
}
else if (value1->isBlob() || value2->isBlob())
{
result->dsc_dtype = dtype_blob;
result->dsc_length = sizeof(ISC_QUAD);
result->setBlobSubType(getResultBlobSubType(value1, value2));
result->setTextType(getResultTextType(value1, value2));
}
else
{
result->dsc_dtype = dtype_varying;
result->setTextType(getResultTextType(value1, value2));
result->setTextType(getResultTextType(value1, value2));
ULONG length = fixLength(result,
convertLength(value1, result) + convertLength(value2, result));
result->dsc_length = length + sizeof(USHORT);
}
result->dsc_flags = (value1->dsc_flags | value2->dsc_flags) & DSC_nullable;
}
void DataTypeUtilBase::makeNullString(dsc* result)
{
clearDsc(result);
// VARCHAR(1) CHARACTER SET NONE
result->dsc_dtype = dtype_varying;
result->setTextType(CS_NONE);
result->dsc_length = sizeof(USHORT) + 1;
result->dsc_flags = DSC_nullable | DSC_null;
}
void DataTypeUtilBase::makeSubstr(dsc* result, const dsc* value)
{
clearDsc(result);
if (value->isBlob())
{
result->dsc_dtype = dtype_blob;
result->dsc_length = sizeof(ISC_QUAD);
result->setBlobSubType(value->getBlobSubType());
}
else
{
// Beware that JRD treats substring() always as returning CHAR
// instead of VARCHAR for historical reasons.
result->dsc_dtype = dtype_varying;
}
result->setTextType(value->getTextType());
result->dsc_flags = value->dsc_flags & DSC_nullable;
if (result->isText())
result->dsc_length = fixLength(result, convertLength(value, result)) + sizeof(USHORT);
}
namespace Jrd {
UCHAR DataTypeUtil::maxBytesPerChar(UCHAR charSet)
{
return INTL_charset_lookup(tdbb, charSet)->maxBytesPerChar();
}

View File

@ -29,15 +29,45 @@
struct dsc;
namespace Jrd {
class DataTypeUtil
class DataTypeUtilBase
{
public:
static SSHORT getResultBlobSubType(const dsc* value1, const dsc* value2);
static USHORT getResultTextType(const dsc* value1, const dsc* value2);
static void makeConcatenate(dsc* result, const dsc* value1, const dsc* value2);
private:
static void clearDsc(dsc* desc);
public:
ULONG convertLength(const dsc* src, const dsc* dst);
ULONG fixLength(const dsc* desc, ULONG length);
void makeConcatenate(dsc* result, const dsc* value1, const dsc* value2);
void makeNullString(dsc* result);
void makeSubstr(dsc* result, const dsc* value);
public:
virtual UCHAR maxBytesPerChar(UCHAR charSet) = 0;
};
namespace Jrd {
class thread_db;
class DataTypeUtil : public DataTypeUtilBase
{
public:
DataTypeUtil(thread_db* tdbb)
: tdbb(tdbb)
{
}
public:
virtual UCHAR maxBytesPerChar(UCHAR charSet);
private:
thread_db* tdbb;
};
} // namespace Jrd

View File

@ -1564,42 +1564,7 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC * de
DSC desc1, desc2;
CMP_get_desc(tdbb, csb, node->nod_arg[0], &desc1);
CMP_get_desc(tdbb, csb, node->nod_arg[1], &desc2);
DataTypeUtil::makeConcatenate(desc, &desc1, &desc2);
if (desc->dsc_dtype == dtype_varying)
{
ULONG len1;
if (desc1.dsc_dtype <= dtype_varying)
{
len1 = DSC_string_length(&desc1) /
INTL_charset_lookup(tdbb, desc1.getCharSet())->maxBytesPerChar();
}
else
len1 = DSC_convert_to_text_length(desc1.dsc_dtype);
ULONG len2;
if (desc2.dsc_dtype <= dtype_varying)
{
len2 = DSC_string_length (&desc2) /
INTL_charset_lookup(tdbb, desc2.getCharSet())->maxBytesPerChar();
}
else
len2 = DSC_convert_to_text_length(desc2.dsc_dtype);
ULONG len = (len1 + len2) *
INTL_charset_lookup(tdbb, desc->getCharSet())->maxBytesPerChar();
if (len > MAX_COLUMN_SIZE - sizeof(USHORT))
{
len = MAX_COLUMN_SIZE - sizeof(USHORT);
ERR_post_warning(isc_concat_overflow, 0);
}
desc->dsc_length = static_cast<USHORT>(len) + sizeof(USHORT);
}
DataTypeUtil(tdbb).makeConcatenate(desc, &desc1, &desc2);
return;
}
@ -1764,7 +1729,7 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC * de
jrd_nod* length_node = node->nod_arg[2];
CMP_get_desc(tdbb, csb, length_node, &desc2);
ULONG rc_len = 0;
DataTypeUtil(tdbb).makeSubstr(desc, &desc1);
if (desc1.dsc_flags & DSC_null || desc2.dsc_flags & DSC_null)
{
@ -1799,33 +1764,9 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC * de
ERR_post(isc_bad_substring_length,
isc_arg_number, length, 0);
}
// Set up the given length
rc_len = length;
}
}
if (desc->dsc_dtype == dtype_blob)
{
if (!rc_len && !(desc->dsc_flags & DSC_null))
{
// We don't know how big will the resulting string be
rc_len = MAX_COLUMN_SIZE - sizeof(USHORT);
}
desc->dsc_dtype = dtype_varying;
desc->dsc_ttype() = desc->dsc_blob_ttype();
desc->dsc_scale = 0;
desc->dsc_length = static_cast<USHORT>(rc_len) + sizeof(USHORT);
}
else if (!DTYPE_IS_TEXT(desc->dsc_dtype))
{
if (!rc_len && !(desc->dsc_flags & DSC_null))
rc_len = DSC_string_length(desc);
desc->dsc_dtype = dtype_varying;
desc->dsc_ttype() = ttype_ascii;
desc->dsc_scale = 0;
desc->dsc_length = static_cast<USHORT>(rc_len) + sizeof(USHORT);
}
return;
}

View File

@ -745,6 +745,12 @@ static bool validate_dsc_tables();
int dsc::getStringLength() const
{
return DSC_string_length(this);
}
USHORT DSC_convert_to_text_length(USHORT dsc_type)
{
if (dsc_type < (sizeof(_DSC_convert_to_text_length) /

View File

@ -101,6 +101,11 @@ typedef struct dsc
return dsc_dtype == dtype_blob || dsc_dtype == dtype_quad;
}
bool isNull() const
{
return dsc_flags & DSC_null;
}
bool isText() const
{
return (dsc_dtype >= dtype_text) && (dsc_dtype <= dtype_varying);
@ -162,6 +167,8 @@ typedef struct dsc
dsc_flags = (dsc_flags & 0xFF) | (ttype & 0xFF00);
}
}
int getStringLength() const;
#endif
// this functions were added to have interoperability

View File

@ -2852,7 +2852,7 @@ static dsc* concatenate(thread_db* tdbb,
SET_TDBB(tdbb);
dsc desc;
DataTypeUtil::makeConcatenate(&desc, value1, value2);
DataTypeUtil(tdbb).makeConcatenate(&desc, value1, value2);
// Both values are present; build the concatenation
@ -4804,12 +4804,12 @@ static dsc* substring(thread_db* tdbb, impure_value* impure,
*
**************************************/
SET_TDBB(tdbb);
dsc desc;
desc.dsc_dtype = dtype_text;
desc.dsc_scale = 0;
DataTypeUtil(tdbb).makeSubstr(&desc, value);
ULONG offset = (ULONG) offset_arg;
const ULONG length = (ULONG) length_arg;
ULONG length = (ULONG) length_arg;
if (offset_arg < 0 || offset > MAX_COLUMN_SIZE)
{
ERR_post(isc_bad_substring_offset, isc_arg_number, offset_arg + 1, 0);
@ -4818,142 +4818,141 @@ static dsc* substring(thread_db* tdbb, impure_value* impure,
{
ERR_post(isc_bad_substring_length, isc_arg_number, length_arg, 0);
}
ULONG ul;
if (value->dsc_dtype == dtype_blob ||
value->dsc_dtype == dtype_quad)
TextType* textType = INTL_texttype_lookup(tdbb, value->getTextType());
CharSet* charSet = textType->getCharSet();
ULONG dataLen;
if (value->isBlob())
{
/* Source string is a blob, things get interesting. */
// Source string is a blob, things get interesting.
TextType* textType = INTL_texttype_lookup(tdbb,
(value->dsc_sub_type == isc_blob_text ? value->dsc_blob_ttype() : ttype_binary));
CharSet* charSet = textType->getCharSet();
fb_assert(desc.dsc_dtype == dtype_blob);
desc.dsc_address = (UCHAR*)&impure->vlu_misc.vlu_bid;
blb* newBlob = BLB_create(tdbb, tdbb->tdbb_request->req_transaction,
&impure->vlu_misc.vlu_bid);
blb* blob = BLB_open(tdbb, tdbb->tdbb_request->req_transaction,
reinterpret_cast<bid*>(value->dsc_address));
if (!blob->blb_length || blob->blb_length <= offset)
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer;
const ULONG totLen = length * charSet->maxBytesPerChar();
if (charSet->isMultiByte())
{
desc.dsc_length = 0;
INTL_ASSIGN_TTYPE(&desc, value->dsc_blob_ttype());
BLB_close(tdbb, blob);
EVL_make_value(tdbb, &desc, impure);
buffer.getBuffer(MIN(blob->blb_length, (offset + length) * charSet->maxBytesPerChar()));
dataLen = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCount(), false);
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer2;
buffer2.getBuffer(dataLen);
if ((dataLen = charSet->substring(tdbb, dataLen, buffer.begin(),
buffer2.getCapacity(), buffer2.begin(),
offset, length)) == INTL_BAD_STR_LENGTH)
{
ERR_post(isc_arith_except, 0);
}
else
BLB_put_data(tdbb, newBlob, buffer2.begin(), dataLen);
}
else
{
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer;
offset *= charSet->maxBytesPerChar();
length *= charSet->maxBytesPerChar();
ULONG datalen;
const ULONG totLen = MIN(MAX_COLUMN_SIZE, length * charSet->maxBytesPerChar());
if (charSet->isMultiByte())
while (!(blob->blb_flags & BLB_eof) && offset)
{
buffer.getBuffer(MIN(blob->blb_length, (offset + length) * charSet->maxBytesPerChar()));
datalen = BLB_get_data(tdbb, blob, buffer.begin(), buffer.getCount());
desc.dsc_length = totLen;
desc.dsc_address = NULL;
INTL_ASSIGN_TTYPE(&desc, value->dsc_blob_ttype());
EVL_make_value(tdbb, &desc, impure);
if ((ul = charSet->substring(tdbb, datalen, buffer.begin(),
desc.dsc_length, impure->vlu_desc.dsc_address,
offset, length)) == INTL_BAD_STR_LENGTH)
{
ERR_post(isc_arith_except, 0);
}
else
impure->vlu_desc.dsc_length = static_cast<USHORT>(ul);
// Both cases are the same for now. Let's see if we can optimize in the future.
ULONG l1 = BLB_get_data(tdbb, blob, buffer.begin(),
MIN(buffer.getCapacity(), offset), false);
offset -= l1;
}
else
while (!(blob->blb_flags & BLB_eof) && length)
{
while (!(blob->blb_flags & BLB_eof) && offset)
{
/* Both cases are the same for now. Let's see if we can optimize in the future. */
ULONG waste = MIN(buffer.getCapacity(), offset * charSet->maxBytesPerChar());
ULONG l1 = BLB_get_data(tdbb, blob, buffer.begin(), waste, false);
offset -= l1 / charSet->maxBytesPerChar();
}
dataLen = BLB_get_data(tdbb, blob, buffer.begin(),
MIN(length, buffer.getCapacity()), false);
length -= dataLen;
fb_assert(!offset && !(blob->blb_flags & BLB_eof));
datalen = BLB_get_data(tdbb, blob, buffer.getBuffer(totLen), totLen);
fb_assert(datalen <= totLen);
desc.dsc_length = datalen;
desc.dsc_address = buffer.begin();
INTL_ASSIGN_TTYPE(&desc, value->dsc_blob_ttype());
EVL_make_value(tdbb, &desc, impure);
BLB_put_data(tdbb, newBlob, buffer.begin(), dataLen);
}
}
return &impure->vlu_desc;
}
BLB_close(tdbb, blob);
BLB_close(tdbb, newBlob);
/* CVC: I didn't bother to define a larger buffer because:
- Native types when converted to string don't reach 31 bytes plus terminator.
- String types do not need and do not use the buffer ("temp") to be pulled.
- The types that can cause an error() issued inside the low level MOV/CVT
routines because the "temp" is not enough are blob and array but at this time
they aren't accepted, so they will cause error() to be called anyway.
*/
UCHAR temp[32];
USHORT ttype;
desc.dsc_length =
MOV_get_string_ptr(value, &ttype, &desc.dsc_address,
reinterpret_cast<vary*>(temp), sizeof(temp));
INTL_ASSIGN_TTYPE(&desc, ttype);
// CVC: Why bother? If the offset is greater or equal than the length in bytes,
// it's impossible that the offset be less than the length in an international charset.
if (offset >= desc.dsc_length || !length)
{
desc.dsc_length = 0;
EVL_make_value(tdbb, &desc, impure);
}
/* CVC: God save the king if the engine doesn't protect itself against buffer overruns,
because intl.h defines UNICODE as the type of most system relations' string fields.
Also, the field charset can come as 127 (dynamic) when it comes from system triggers,
but it's resolved by INTL_obj_lookup() to UNICODE_FSS in the cases I observed. Here I cannot
distinguish between user calls and system calls. Unlike the original ASCII substring(),
this one will get correctly the amount of UNICODE characters requested. */
else if (desc.dsc_ttype() == ttype_ascii || desc.dsc_ttype() == ttype_none
|| ttype == ttype_binary
/*|| desc.dsc_ttype() == ttype_metadata) */)
{
/* Redundant.
if (offset >= desc.dsc_length)
desc.dsc_length = 0;
else */
desc.dsc_address += offset;
desc.dsc_length -= offset;
if (length < desc.dsc_length)
desc.dsc_length = length;
EVL_make_value(tdbb, &desc, impure);
}
else
{
// CVC: ATTENTION:
// I couldn't find an appropriate message for this failure among current registered
// messages, so I will return empty.
// Finally I decided to use arithmetic exception or numeric overflow.
const UCHAR* p = desc.dsc_address;
USHORT pcount = desc.dsc_length;
fb_assert(desc.isText());
TextType* textType = INTL_texttype_lookup(tdbb, INTL_TTYPE(&desc));
CharSet* charSet = textType->getCharSet();
desc.dsc_dtype = dtype_text;
desc.dsc_address = NULL;
const ULONG totLen = MIN(MAX_COLUMN_SIZE, length * charSet->maxBytesPerChar());
desc.dsc_length = totLen;
EVL_make_value(tdbb, &desc, impure);
// CVC: I didn't bother to define a larger buffer because:
// - Native types when converted to string don't reach 31 bytes plus terminator.
// - String types do not need and do not use the buffer ("temp") to be pulled.
// - The types that can cause an error() issued inside the low level MOV/CVT
// routines because the "temp" is not enough are blob and array but at this time
// they aren't accepted, so they will cause error() to be called anyway.
UCHAR temp[32];
USHORT ttype;
desc.dsc_length =
MOV_get_string_ptr(value, &ttype, &desc.dsc_address,
reinterpret_cast<vary*>(temp), sizeof(temp));
desc.setTextType(ttype);
if ((ul = charSet->substring(tdbb, pcount, p,
totLen,
impure->vlu_desc.dsc_address, offset, length)) == INTL_BAD_STR_LENGTH)
// CVC: Why bother? If the offset is greater or equal than the length in bytes,
// it's impossible that the offset be less than the length in an international charset.
if (offset >= desc.dsc_length || !length)
{
ERR_post(isc_arith_except, 0);
desc.dsc_length = 0;
EVL_make_value(tdbb, &desc, impure);
}
// CVC: God save the king if the engine doesn't protect itself against buffer overruns,
// because intl.h defines UNICODE as the type of most system relations' string fields.
// Also, the field charset can come as 127 (dynamic) when it comes from system triggers,
// but it's resolved by INTL_obj_lookup() to UNICODE_FSS in the cases I observed. Here I cannot
// distinguish between user calls and system calls. Unlike the original ASCII substring(),
// this one will get correctly the amount of UNICODE characters requested.
else if (ttype == ttype_ascii || ttype == ttype_none || ttype == ttype_binary)
{
/* Redundant.
if (offset >= desc.dsc_length)
desc.dsc_length = 0;
else */
desc.dsc_address += offset;
desc.dsc_length -= offset;
if (length < desc.dsc_length)
desc.dsc_length = length;
EVL_make_value(tdbb, &desc, impure);
}
else
impure->vlu_desc.dsc_length = static_cast<USHORT>(ul);
{
// CVC: ATTENTION:
// I couldn't find an appropriate message for this failure among current registered
// messages, so I will return empty.
// Finally I decided to use arithmetic exception or numeric overflow.
const UCHAR* p = desc.dsc_address;
USHORT pcount = desc.dsc_length;
CharSet* charSet = INTL_charset_lookup(tdbb, desc.getCharSet());
desc.dsc_address = NULL;
const ULONG totLen = MIN(MAX_COLUMN_SIZE, length * charSet->maxBytesPerChar());
desc.dsc_length = totLen;
EVL_make_value(tdbb, &desc, impure);
if ((dataLen = charSet->substring(tdbb, pcount, p, totLen,
impure->vlu_desc.dsc_address, offset, length)) == INTL_BAD_STR_LENGTH)
{
ERR_post(isc_arith_except, 0);
}
else
impure->vlu_desc.dsc_length = static_cast<USHORT>(dataLen);
}
}
return &impure->vlu_desc;