From 4180a46169b7f9d9488765c3729dabd2ad1c8535 Mon Sep 17 00:00:00 2001 From: asfernandes Date: Sat, 12 Aug 2006 03:17:01 +0000 Subject: [PATCH] 1) Make SUBSTRING of BLOB work 2) More refactoration on describing result of expressions (concatenate and substring) --- src/dsql/make.cpp | 96 ++++------------- src/dsql/utld.cpp | 13 +++ src/dsql/utld_proto.h | 27 +++++ src/jrd/DataTypeUtil.cpp | 112 +++++++++++++++++--- src/jrd/DataTypeUtil.h | 38 ++++++- src/jrd/cmp.cpp | 63 +---------- src/jrd/dsc.cpp | 6 ++ src/jrd/dsc.h | 7 ++ src/jrd/evl.cpp | 221 +++++++++++++++++++-------------------- 9 files changed, 316 insertions(+), 267 deletions(-) diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index c1f024f241..482d0c3592 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -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); diff --git a/src/dsql/utld.cpp b/src/dsql/utld.cpp index 14c7298da7..dd1cda0886 100644 --- a/src/dsql/utld.cpp +++ b/src/dsql/utld.cpp @@ -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 diff --git a/src/dsql/utld_proto.h b/src/dsql/utld_proto.h index 09be787e4f..37b5fbf89f 100644 --- a/src/dsql/utld_proto.h +++ b/src/dsql/utld_proto.h @@ -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 diff --git a/src/jrd/DataTypeUtil.cpp b/src/jrd/DataTypeUtil.cpp index fa421144f9..52484c3624 100644 --- a/src/jrd/DataTypeUtil.cpp +++ b/src/jrd/DataTypeUtil.cpp @@ -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(); } diff --git a/src/jrd/DataTypeUtil.h b/src/jrd/DataTypeUtil.h index af5dd73494..5060a9c0e3 100644 --- a/src/jrd/DataTypeUtil.h +++ b/src/jrd/DataTypeUtil.h @@ -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 diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 09871eeb1b..6aea74881f 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -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(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(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(rc_len) + sizeof(USHORT); - } return; } diff --git a/src/jrd/dsc.cpp b/src/jrd/dsc.cpp index 1fdeef0c52..b4ef02202a 100644 --- a/src/jrd/dsc.cpp +++ b/src/jrd/dsc.cpp @@ -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) / diff --git a/src/jrd/dsc.h b/src/jrd/dsc.h index 971ddc1f66..f8531f6e66 100644 --- a/src/jrd/dsc.h +++ b/src/jrd/dsc.h @@ -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 diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index f8057edaeb..bce44b4900 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -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(value->dsc_address)); - if (!blob->blb_length || blob->blb_length <= offset) + + Firebird::HalfStaticArray 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 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 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(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(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(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(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(dataLen); + } } return &impure->vlu_desc;