From 48e2d3207618a75a6f8a042a8568bde889a8c5e7 Mon Sep 17 00:00:00 2001 From: Dimitry Sibiryakov Date: Mon, 19 May 2025 16:11:05 +0200 Subject: [PATCH] Recognize message format with a real data length for dtype_text --- src/dsql/ExprNodes.cpp | 39 +++++++++++++++++++++++++++++++++++---- src/dsql/ExprNodes.h | 1 + 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 31973dfcd4e..c2f2eb4905e 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -9881,6 +9881,19 @@ ParameterNode* ParameterNode::pass1(thread_db* tdbb, CompilerScratch* csb) status_exception::raise(Arg::Gds(isc_ctxnotdef) << Arg::Gds(isc_random) << Arg::Str("Outer parameter has no outer scratch")); } + const dsc& desc = format->fmt_desc[argNumber]; + if (desc.isText()) + { + // Remember expected maximum length in characters to be able to recognize format of the real data buffer later + const CharSet* charSet = INTL_charset_lookup(tdbb, desc.getCharSet()); + USHORT length = TEXT_LEN(&desc); + if (charSet->isMultiByte()) + { + length /= charSet->maxBytesPerChar(); + } + maxCharLength = length; + } + return this; } @@ -9944,9 +9957,6 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const { if (impureForOuter) EVL_make_value(tdbb, retDesc, impureForOuter); - - if (retDesc->dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, retDesc); } auto impureFlags = paramRequest->getImpure( @@ -9984,7 +9994,9 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const auto charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(retDesc)); EngineCallbacks::instance->validateData(charSet, len, p); - EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(retDesc), len, p, maxLen); + + // Validation of length for user-provided data against user-provided metadata makes a little sense here. Leave it to the real assignment. + // Besides in some cases overlong values are valid. For example `field like ?` } else if (retDesc->isBlob()) { @@ -10013,6 +10025,25 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const *impureFlags |= VLU_checked; } + // This block is after validation because having here a malformed data would produce a wrong result + if (!(request->req_flags & req_null) && retDesc->dsc_dtype == dtype_text && maxCharLength != 0) + { + // Data in the message buffer can be in a padded Firebird format or in an application-defined format with real length. + // API provides no way to distinguish these cases so we must use some heuristics: + // perform the adjustment only if the data length matches the length that would be expected in the padded format. + + const CharSet* charSet = INTL_charset_lookup(tdbb, retDesc->getCharSet()); + + if (charSet->isMultiByte() && maxCharLength * charSet->maxBytesPerChar() == retDesc->dsc_length) + { + Firebird::HalfStaticArray buffer; + + retDesc->dsc_length = charSet->substring(retDesc->dsc_length, retDesc->dsc_address, + retDesc->dsc_length, buffer.getBuffer(retDesc->dsc_length), 0, + maxCharLength); + } + } + return (request->req_flags & req_null) ? nullptr : retDesc; } diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 9a7166740fe..906eda67290 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -1671,6 +1671,7 @@ class ParameterNode final : public TypedNode