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:
parent
e5e678f587
commit
4180a46169
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) /
|
||||
|
@ -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
|
||||
|
221
src/jrd/evl.cpp
221
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<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;
|
||||
|
Loading…
Reference in New Issue
Block a user