diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 788d473a8a8..ce45ea3c1e2 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -1648,7 +1648,7 @@ double CVT_get_double(const dsc* desc, DecimalStatus decSt, ErrorFunction err, b } -void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* cb) +void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* cb, bool trustedSource) { /************************************** * @@ -1669,7 +1669,13 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c // optimal, it would cost more to find the fast move than the // fast move would gain. - if (DSC_EQUIV(from, to, false)) + // But do not do it for strings because their length has not been validated until this moment + // (real source length must be validated against target maximum length + // and this is the first common place where both are present). + + // ...unless these strings are coming from a trusted source (for example a cached record buffer) + + if (DSC_EQUIV(from, to, false) && (trustedSource || !DTYPE_IS_TEXT(from->dsc_dtype))) { if (length) { memcpy(p, q, length); @@ -1983,6 +1989,9 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c if (cb->transliterate(from, to, charset2)) return; + // At this point both `from` and `to` are guaranteed to have the same charset and this is stored in charset2 + // Because of this we can freely use `toCharset` against `from`. + { // scope USHORT strtype_unused; UCHAR *ptr; @@ -1993,8 +2002,23 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c const USHORT to_size = TEXT_LEN(to); CharSet* toCharset = cb->getToCharset(charset2); - cb->validateData(toCharset, length, q); - ULONG toLength = cb->validateLength(toCharset, charset2, length, q, to_size); + ULONG toLength = length; + + if (!trustedSource) + { + // Most likely data already has been validated once or twice, but another validation won't hurt much. + cb->validateData(toCharset, length, q); + toLength = cb->validateLength(toCharset, charset2, length, q, to_size); + } + else + { + // Silently truncate. In the wild this should never happen + if (length > to_size) + { + fb_assert(from->dsc_dtype == dtype_text); + toLength = to_size; + } + } switch (to->dsc_dtype) { @@ -3772,7 +3796,7 @@ USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, } -void CVT_move(const dsc* from, dsc* to, DecimalStatus decSt, ErrorFunction err) +void CVT_move(const dsc* from, dsc* to, DecimalStatus decSt, ErrorFunction err, bool trustedSource) { /************************************** * @@ -3785,5 +3809,5 @@ void CVT_move(const dsc* from, dsc* to, DecimalStatus decSt, ErrorFunction err) * **************************************/ CommonCallbacks callbacks(err); - CVT_move_common(from, to, decSt, &callbacks); + CVT_move_common(from, to, decSt, &callbacks, trustedSource); } diff --git a/src/common/cvt.h b/src/common/cvt.h index da95c1bc734..0a4198fc215 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -95,8 +95,8 @@ Firebird::Decimal128 CVT_get_dec128(const dsc*, Firebird::DecimalStatus, ErrorFu Firebird::Int128 CVT_get_int128(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); Firebird::Int128 CVT_hex_to_int128(const char* str, USHORT len); USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); -void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*); -void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction); +void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*, bool trustedSource = false); +void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction, bool trustedSource = false); SSHORT CVT_decompose(const char*, USHORT, Firebird::Int128*, ErrorFunction); USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 31973dfcd4e..d12d1fcbebe 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( @@ -9956,7 +9966,6 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const { if (!(request->req_flags & req_null)) { - USHORT maxLen = desc->dsc_length; // not adjusted length if (DTYPE_IS_TEXT(retDesc->dsc_dtype)) { @@ -9966,8 +9975,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const switch (retDesc->dsc_dtype) { case dtype_cstring: - len = static_cast(strnlen((const char*) p, maxLen)); - --maxLen; + len = static_cast(strnlen((const char*) p, desc->dsc_length)); break; case dtype_text: @@ -9977,14 +9985,15 @@ dsc* ParameterNode::execute(thread_db* tdbb, Request* request) const case dtype_varying: len = reinterpret_cast(p)->vary_length; p += sizeof(USHORT); - maxLen -= sizeof(USHORT); break; } 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 +10022,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 TypedNodereq_ports; port; port = port->por_next) cmp_port(port, request); - if (request->req_values) - { - request->add_byte(blr_begin); - make_send(request->req_vport, request); - } - if (gpreGlob.sw_ids) { request->add_byte(blr_exec_pid); @@ -1246,8 +1240,6 @@ static void cmp_procedure( gpre_req* request) else request->add_word(0); - if (request->req_values) - request->add_byte(blr_end); request->add_byte(blr_end); request->add_byte(blr_eoc); request->req_length = request->req_blr - request->req_base; diff --git a/src/jrd/cvt2.cpp b/src/jrd/cvt2.cpp index f6b4e0979ff..999ad88b111 100644 --- a/src/jrd/cvt2.cpp +++ b/src/jrd/cvt2.cpp @@ -490,6 +490,17 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt switch (arg1->dsc_dtype) { case dtype_ex_timestamp_tz: + { + DSC desc; + MOVE_CLEAR(&desc, sizeof(desc)); + desc.dsc_dtype = dtype_ex_timestamp_tz; + ISC_TIMESTAMP_TZ_EX datetime; + desc.dsc_length = sizeof(datetime); + desc.dsc_address = (UCHAR*) &datetime; + CVT_move(arg2, &desc, 0); + return CVT2_compare(arg1, &desc, 0); + } + case dtype_timestamp_tz: { DSC desc; @@ -515,6 +526,17 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt } case dtype_ex_time_tz: + { + DSC desc; + MOVE_CLEAR(&desc, sizeof(desc)); + desc.dsc_dtype = dtype_ex_time_tz; + ISC_TIME_TZ_EX atime; + desc.dsc_length = sizeof(atime); + desc.dsc_address = (UCHAR*) &atime; + CVT_move(arg2, &desc, 0); + return CVT2_compare(arg1, &desc, 0); + } + case dtype_sql_time_tz: { DSC desc; diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index dc925c102e1..6876ad0150a 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -462,6 +462,8 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo } } + // Strings will be validated in CVT_move() + if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) || DTYPE_IS_BLOB_OR_QUAD(to_desc->dsc_dtype)) { // ASF: Don't let MOV_move call blb::move because MOV @@ -493,6 +495,11 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo { MOV_move(tdbb, from_desc, to_desc); } + else if (DTYPE_IS_TEXT(from_desc->dsc_dtype)) + { + // Force slow move to properly handle the case when source string is provided with real length instead of padded length + MOV_move(tdbb, from_desc, to_desc); + } else if (from_desc->dsc_dtype == dtype_short) { *((SSHORT*) to_desc->dsc_address) = *((SSHORT*) from_desc->dsc_address); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index f6ca5a2fb00..1ccb35571c7 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -4104,6 +4104,26 @@ void JRequest::getInfo(CheckStatusWrapper* user_status, int level, unsigned int try { + for (unsigned i = 0; i < itemsLength; ++i) + { + if (items[i] == isc_info_message_number || items[i] == isc_info_message_size) + { + // For proper return these items require request operation req_send or req_receive + // Run request from stale status until we get one of these (or end of program) + // It is up to caller to make sure that there is no heavy operations between req_next + // and the next SuspendNode/ReceiveNode/SelectMessageNode + while ((request->req_flags & req_active) + && request->req_operation != Request::req_receive + && request->req_operation != Request::req_send) + { + request->req_flags &= ~req_stall; + request->req_operation = Request::req_sync; + EXE_looper(tdbb, request, request->req_next); + } + break; + } + } + INF_request_info(request, itemsLength, items, bufferLength, buffer); } catch (const Exception& ex) @@ -4803,6 +4823,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* JTransaction* const jt = getTransactionInterface(user_status, tra); EngineContextHolder tdbb(user_status, this, FB_FUNCTION); + Request* request = nullptr; jrd_tra* transaction = jt->getHandle(); validateHandle(tdbb, transaction); check_database(tdbb); @@ -4814,7 +4835,6 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* const MessageNode* inMessage = NULL; const MessageNode* outMessage = NULL; - Request* request = NULL; MemoryPool* new_pool = att->createPool(); try @@ -4840,9 +4860,7 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* } catch (const Exception&) { - if (request) - CMP_release(tdbb, request); - else + if (!request) att->deletePool(new_pool); throw; @@ -4875,6 +4893,15 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* if (out_msg_length) { + // Workaround for GPRE that generated unneeded blr_send + if ((request->req_flags & req_active) + && request->req_operation == Request::req_send) + { + request->req_flags &= ~req_stall; + request->req_operation = Request::req_proceed; + EXE_looper(tdbb, request, request->req_next); + } + memcpy(out_msg, outMessage->getBuffer(request), out_msg_length); } @@ -4884,6 +4911,9 @@ void JAttachment::transactRequest(CheckStatusWrapper* user_status, ITransaction* } catch (const Exception& ex) { + if (request) + CMP_release(tdbb, request); + transliterateException(tdbb, ex, user_status, "JAttachment::transactRequest"); return; } diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 3f03c8f79cd..1b398b59bf9 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -438,7 +438,7 @@ Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT } -void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to) +void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to, bool trustedSource) { /************************************** * @@ -454,7 +454,7 @@ void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to) if (DTYPE_IS_BLOB_OR_QUAD(from->dsc_dtype) || DTYPE_IS_BLOB_OR_QUAD(to->dsc_dtype)) Jrd::blb::move(tdbb, from, to); else - CVT_move(from, to, tdbb->getAttachment()->att_dec_status); + CVT_move_common(from, to, tdbb->getAttachment()->att_dec_status, &Jrd::EngineCallbacks::instance, trustedSource); } diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index d2200f6f6d6..1ba62a97381 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -52,7 +52,7 @@ USHORT MOV_make_string(Jrd::thread_db*, const dsc*, USHORT, const char**, vary*, ULONG MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, bool = true); Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, bool limit = true); -void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*); +void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*, bool trustedSource = false); Firebird::Decimal64 MOV_get_dec64(Jrd::thread_db*, const dsc*); Firebird::Decimal128 MOV_get_dec128(Jrd::thread_db*, const dsc*); Firebird::Int128 MOV_get_int128(Jrd::thread_db*, const dsc*, SSHORT); diff --git a/src/jrd/recsrc/BufferedStream.cpp b/src/jrd/recsrc/BufferedStream.cpp index 0aef2874a8f..2c24d54f1c6 100644 --- a/src/jrd/recsrc/BufferedStream.cpp +++ b/src/jrd/recsrc/BufferedStream.cpp @@ -199,7 +199,7 @@ bool BufferedStream::internalGetRecord(thread_db* tdbb) const switch (map.map_type) { case FieldMap::REGULAR_FIELD: - MOV_move(tdbb, &from, &to); + MOV_move(tdbb, &from, &to, true); break; case FieldMap::TRANSACTION_ID: @@ -271,7 +271,7 @@ bool BufferedStream::internalGetRecord(thread_db* tdbb) const else { EVL_field(relation, record, map.map_id, &to); - MOV_move(tdbb, &from, &to); + MOV_move(tdbb, &from, &to, true); record->clearNull(map.map_id); } diff --git a/src/jrd/recsrc/HashJoin.cpp b/src/jrd/recsrc/HashJoin.cpp index 3ba9941c8ca..a0207735fac 100644 --- a/src/jrd/recsrc/HashJoin.cpp +++ b/src/jrd/recsrc/HashJoin.cpp @@ -687,7 +687,7 @@ ULONG HashJoin::computeHash(thread_db* tdbb, else { // This call ensures that the padding bytes are appended - MOV_move(tdbb, desc, &to); + MOV_move(tdbb, desc, &to, true); } } else diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index 911602b4f37..6c81be3598e 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -278,7 +278,7 @@ Sort* SortedStream::init(thread_db* tdbb) const } else { - MOV_move(tdbb, from, &to); + MOV_move(tdbb, from, &to, true); } } } @@ -438,7 +438,7 @@ void SortedStream::mapData(thread_db* tdbb, Request* request, UCHAR* data) const else { EVL_field(relation, record, id, &to); - MOV_move(tdbb, &from, &to); + MOV_move(tdbb, &from, &to, true); record->clearNull(id); } } diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index 9f8bf9b1a11..511b8bef411 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -574,6 +574,9 @@ void WindowedStream::WindowStream::internalOpen(thread_db* tdbb) const if (m_invariantOffsets & 0x2) getFrameValue(tdbb, request, m_frameExtent->frame2, &impure->endOffset); + + // Make initial values for partitioning fields clean. + request->req_rpb[m_stream].rpb_record->nullify(); } void WindowedStream::WindowStream::close(thread_db* tdbb) const @@ -886,7 +889,7 @@ bool WindowedStream::WindowStream::internalGetRecord(thread_db* tdbb) const record->setNull(id); else { - MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target)); + MOV_move(tdbb, desc, EVL_assign_to(tdbb, *target), true); record->clearNull(id); } diff --git a/src/remote/remote.h b/src/remote/remote.h index 186c38cc9f2..8d5efe6e416 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -1704,7 +1704,7 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted ISC_STATUS put_segment(P_OP, P_SGMT*, PACKET*); ISC_STATUS put_slice(P_SLC*, PACKET*); ISC_STATUS que_events(P_EVENT*, PACKET*); - ISC_STATUS receive_after_start(P_DATA*, PACKET*, Firebird::IStatus*); + ISC_STATUS receive_after_start(P_DATA* data, PACKET* sendL, Firebird::CheckStatusWrapper* status_vector); ISC_STATUS receive_msg(P_DATA*, PACKET*); ISC_STATUS seek_blob(P_SEEK*, PACKET*); ISC_STATUS send_msg(P_DATA*, PACKET*); diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index b0753656c95..6ee13a13929 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -1275,10 +1275,10 @@ static void addClumplets(ClumpletWriter*, const ParametersSet&, const rem_port* static void cancel_operation(rem_port*, USHORT); -static bool check_request(Rrq*, USHORT, USHORT); +static bool check_request(Rrq* request, USHORT incarnation, USHORT msg_number, CheckStatusWrapper* status); static USHORT check_statement_type(Rsr*); -static bool get_next_msg_no(Rrq*, USHORT, USHORT*); +static bool get_next_msg_no(Rrq* request, USHORT incarnation, USHORT* msg_number, CheckStatusWrapper* status); static Rtr* make_transaction(Rdb*, ITransaction*); static void ping_connection(rem_port*, PACKET*); static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_port** result); @@ -2819,7 +2819,7 @@ static void cancel_operation(rem_port* port, USHORT kind) } -static bool check_request(Rrq* request, USHORT incarnation, USHORT msg_number) +static bool check_request(Rrq* request, USHORT incarnation, USHORT msg_number, CheckStatusWrapper* status) { /************************************** * @@ -2834,7 +2834,7 @@ static bool check_request(Rrq* request, USHORT incarnation, USHORT msg_number) **************************************/ USHORT n; - if (!get_next_msg_no(request, incarnation, &n)) + if (!get_next_msg_no(request, incarnation, &n, status)) return false; return msg_number == n; @@ -4430,7 +4430,7 @@ ISC_STATUS rem_port::fetch(P_SQLDATA * sqldata, PACKET* sendL, bool scroll) } -static bool get_next_msg_no(Rrq* request, USHORT incarnation, USHORT * msg_number) +static bool get_next_msg_no(Rrq* request, USHORT incarnation, USHORT * msg_number, CheckStatusWrapper* status) { /************************************** * @@ -4443,14 +4443,12 @@ static bool get_next_msg_no(Rrq* request, USHORT incarnation, USHORT * msg_numbe * in the request. * **************************************/ - LocalStatus ls; - CheckStatusWrapper status_vector(&ls); UCHAR info_buffer[128]; - request->rrq_iface->getInfo(&status_vector, incarnation, + request->rrq_iface->getInfo(status, incarnation, sizeof(request_info), request_info, sizeof(info_buffer), info_buffer); - if (status_vector.getState() & IStatus::STATE_ERRORS) + if (status->getState() & IStatus::STATE_ERRORS) return false; bool result = false; @@ -5614,7 +5612,7 @@ ISC_STATUS rem_port::que_events(P_EVENT * stuff, PACKET* sendL) } -ISC_STATUS rem_port::receive_after_start(P_DATA* data, PACKET* sendL, IStatus* status_vector) +ISC_STATUS rem_port::receive_after_start(P_DATA* data, PACKET* sendL, CheckStatusWrapper* status_vector) { /************************************** * @@ -5636,7 +5634,7 @@ ISC_STATUS rem_port::receive_after_start(P_DATA* data, PACKET* sendL, IStatus* s // Figure out the number of the message that we're stalled on. USHORT msg_number; - if (!get_next_msg_no(requestL, level, &msg_number)) + if (!get_next_msg_no(requestL, level, &msg_number, status_vector)) return this->send_response(sendL, 0, 0, status_vector, false); sendL->p_operation = op_response_piggyback; @@ -5751,9 +5749,12 @@ ISC_STATUS rem_port::receive_msg(P_DATA * data, PACKET* sendL) RMessage* next = message->msg_next; if ((next == message || !next->msg_address) && - !check_request(requestL, data->p_data_incarnation, msg_number)) + !check_request(requestL, data->p_data_incarnation, msg_number, &status_vector)) { - // We've reached the end of the RSE - don't prefetch and flush + if (status_vector.getState() & IStatus::STATE_ERRORS) + return this->send_response(sendL, 0, 0, &status_vector, false); + + // We've reached the end of the RSE or ReceiveNode/SelectMessageNode - don't prefetch and flush // everything we've buffered so far count2 = 0; @@ -5784,8 +5785,21 @@ ISC_STATUS rem_port::receive_msg(P_DATA * data, PACKET* sendL) while (message->msg_address && message->msg_next != tail->rrq_xdr) message = message->msg_next; - for (; count2 && check_request(requestL, data->p_data_incarnation, msg_number); --count2) + for (; count2; --count2) { + if (!check_request(requestL, data->p_data_incarnation, msg_number, &status_vector)) + { + if (status_vector.getState() & IStatus::STATE_ERRORS) + { + // If already have an error queued, don't overwrite it + + if (requestL->rrqStatus.isSuccess()) + requestL->rrqStatus.save(&status_vector); + } + + break; + } + if (message->msg_address) { if (!prior)