From ab2fb0bab74830861e36c0ecffeb344b4065bd90 Mon Sep 17 00:00:00 2001 From: Dorin Marcoci Date: Fri, 2 Oct 2026 15:43:17 +0300 Subject: [PATCH] Fix isc_array_lookup_desc/bounds length of text arrays on attachments with another character set Text elements of the array API are exchanged in the attachment character set (blr_text/blr_varying without a character set), but the length was the column length in bytes of the column character set. On a UTF8 attachment a VARCHAR(15) column in NONE or a single-byte character set got 15 bytes, i.e. 3 characters, and isc_array_get_slice/put_slice failed with string right truncation. Use the declared number of characters in the attachment character set when it's longer, as DSQL does for messages. --- src/yvalve/array.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/yvalve/array.cpp b/src/yvalve/array.cpp index c90c195d244..2736afc137a 100644 --- a/src/yvalve/array.cpp +++ b/src/yvalve/array.cpp @@ -61,6 +61,7 @@ struct gen_t static void adjust_length(ISC_ARRAY_DESC*) noexcept; static void copy_exact_name (const char*, char*, SSHORT) noexcept; +static SLONG get_attachment_charset(Why::YAttachment*); static ISC_STATUS error(ISC_STATUS* status, const Arg::StatusVector& v) noexcept; static ISC_STATUS gen_sdl(ISC_STATUS*, const ISC_ARRAY_DESC*, SSHORT*, UCHAR**, SSHORT*, bool); static ISC_STATUS stuff_args(gen_t*, SSHORT, ...); @@ -249,13 +250,17 @@ void iscArrayLookupDescImpl(Why::YAttachment* attachment, f.rdb$field_type, f.rdb$field_scale, f.rdb$field_length, - f.rdb$dimensions + f.rdb$dimensions, + f.rdb$character_length, + cs.rdb$bytes_per_character from search_path sp join system.rdb$relation_fields rf on rf.rdb$schema_name = sp.name join system.rdb$fields f on f.rdb$schema_name = rf.rdb$field_source_schema_name and f.rdb$field_name = rf.rdb$field_source + left join system.rdb$character_sets cs + on cs.rdb$character_set_id = ? where rf.rdb$relation_name = ? and rf.rdb$field_name = ? order by sp.rn @@ -267,10 +272,14 @@ void iscArrayLookupDescImpl(Why::YAttachment* attachment, f.rdb$field_type, f.rdb$field_scale, f.rdb$field_length, - f.rdb$dimensions + f.rdb$dimensions, + f.rdb$character_length, + cs.rdb$bytes_per_character from rdb$relation_fields rf join rdb$fields f on f.rdb$field_name = rf.rdb$field_source + left join rdb$character_sets cs + on cs.rdb$character_set_id = ? where rf.rdb$relation_name = ? and rf.rdb$field_name = ? )"""; @@ -278,6 +287,7 @@ void iscArrayLookupDescImpl(Why::YAttachment* attachment, const auto sql = majorOdsVersion >= ODS_VERSION14 ? sqlSchemas : sqlNoSchemas; FB_MESSAGE(InputMessage, CheckStatusWrapper, + (FB_INTEGER, charSetId) (FB_VARCHAR(MAX_SQL_IDENTIFIER_LEN), relationName) (FB_VARCHAR(MAX_SQL_IDENTIFIER_LEN), fieldName) ) inputMessage(&statusWrapper, MasterInterfacePtr()); @@ -289,8 +299,14 @@ void iscArrayLookupDescImpl(Why::YAttachment* attachment, (FB_INTEGER, fieldScale) (FB_INTEGER, fieldLength) (FB_INTEGER, dimensions) + (FB_INTEGER, characterLength) + (FB_INTEGER, bytesPerCharacter) ) outputMessage(&statusWrapper, MasterInterfacePtr()); + const SLONG charSetId = get_attachment_charset(attachment); + inputMessage->charSetIdNull = charSetId < 0 ? FB_TRUE : FB_FALSE; + inputMessage->charSetId = charSetId; + inputMessage->relationNameNull = FB_FALSE; inputMessage->relationName.set((const char*) relationName); @@ -311,6 +327,24 @@ void iscArrayLookupDescImpl(Why::YAttachment* attachment, desc->array_desc_scale = outputMessage->fieldScaleNull ? 0 : outputMessage->fieldScale; desc->array_desc_length = outputMessage->fieldLengthNull ? 0 : outputMessage->fieldLength; + // Text elements are exchanged in the attachment character set (blr_text, blr_varying), + // so the length must hold the declared number of characters in it, as in DSQL messages. + switch (desc->array_desc_dtype) + { + case blr_text: + case blr_varying: + case blr_cstring: + if (!outputMessage->characterLengthNull && !outputMessage->bytesPerCharacterNull) + { + const ULONG length = MIN((ULONG) outputMessage->characterLength * + outputMessage->bytesPerCharacter, MAX_VARY_COLUMN_SIZE); + + if (length > desc->array_desc_length) + desc->array_desc_length = (USHORT) length; + } + break; + } + adjust_length(desc); desc->array_desc_dimensions = outputMessage->dimensionsNull ? 0 : outputMessage->dimensions; @@ -474,6 +508,34 @@ static void adjust_length(ISC_ARRAY_DESC*) noexcept } +static SLONG get_attachment_charset(Why::YAttachment* attachment) +{ +/************************************** + * + * g e t _ a t t a c h m e n t _ c h a r s e t + * + ************************************** + * + * Functional description + * Return the character set of the attachment or -1 if unknown. + * + **************************************/ + LocalStatus status; + CheckStatusWrapper statusWrapper(&status); + + const UCHAR items[] = {frb_info_att_charset, isc_info_end}; + UCHAR buffer[16]; + + attachment->getInfo(&statusWrapper, sizeof(items), items, sizeof(buffer), buffer); + + if ((status.getState() & IStatus::STATE_ERRORS) || buffer[0] != frb_info_att_charset) + return -1; + + const SSHORT length = (SSHORT) gds__vax_integer(buffer + 1, 2); + return gds__vax_integer(buffer + 3, length); +} + + static void copy_exact_name(const char* from, char* to, SSHORT bsize) noexcept { /**************************************