From 3c22c23874e2212601eb3d31969d7d1b5229aa5f Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Sun, 30 Jan 2022 12:11:05 -0300 Subject: [PATCH] Improvement #4769 - Allow sub-routines to access variables/parameters defined at the outer/parent level [CORE4449]. Remove blr_parameter3. Comment unused blr_run_count. --- doc/sql.extensions/README.subroutines.txt | 9 +- src/dsql/DdlNodes.epp | 6 + src/dsql/DsqlCompilerScratch.cpp | 43 +++- src/dsql/DsqlCompilerScratch.h | 13 +- src/dsql/ExprNodes.cpp | 300 ++++++++++++++++------ src/dsql/ExprNodes.h | 12 +- src/dsql/Nodes.h | 4 +- src/dsql/StmtNodes.cpp | 128 ++++++++- src/dsql/StmtNodes.h | 50 +++- src/dsql/gen.cpp | 8 +- src/include/firebird/impl/blr.h | 8 +- src/jrd/RecordSourceNodes.cpp | 3 - src/jrd/blp.h | 1 + src/jrd/evl.cpp | 34 +-- src/jrd/exe.cpp | 72 ++---- src/jrd/exe.h | 11 +- src/jrd/par.cpp | 1 + src/yvalve/gds.cpp | 44 +++- 18 files changed, 533 insertions(+), 214 deletions(-) diff --git a/doc/sql.extensions/README.subroutines.txt b/doc/sql.extensions/README.subroutines.txt index 5de6db5494..0caeabcabe 100644 --- a/doc/sql.extensions/README.subroutines.txt +++ b/doc/sql.extensions/README.subroutines.txt @@ -11,7 +11,7 @@ Description: Syntax: ::= - DECLARE [VARIABLE] [ := ]; + DECLARE [VARIABLE] [ = ]; | DECLARE [VARIABLE] CURSOR FOR (); | @@ -46,8 +46,11 @@ Syntax: Limitations: 1) Subroutines may not be nested in another subroutine. They are only supported in the main routine. - 2) Currently, a subroutine may not directly access or use variables or cursors of the - main statements. This may be allowed in the future. + 2) Currently, a subroutine may not directly access cursors of the main routine/block. + This may be allowed in the future. + 3) Since FB 5 subroutines may use variables and parameters from the main routine/block. + 4) Variables and parameters that are accessed by subroutines may have a small performance + penalty (even in the main routine) when being read. Notes: 1) Starting in FB 4, subroutines may be recursive or call others subroutines. diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 3be22e61e5..8ebcfca04f 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -2342,6 +2342,8 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -3235,6 +3237,8 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlScratch->cursorNumber = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -3751,6 +3755,8 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d dsqlScratch->scopeLevel++; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); // dimitr: I see no reason to deny EXIT command in triggers, diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index f1550fe348..f3c21dbc06 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -345,30 +345,24 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE)) { // Check not implemented sub-functions. - - GenericMap >::ConstAccessor funcAccessor(&subFunctions); - - for (bool found = funcAccessor.getFirst(); found; found = funcAccessor.getNext()) + for (const auto& funcPair : subFunctions) { - if (!funcAccessor.current()->second->dsqlBlock) + if (!funcPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subfunc_not_impl) << - funcAccessor.current()->first.c_str()); + funcPair.first.c_str()); } } // Check not implemented sub-procedures. - - GenericMap >::ConstAccessor procAccessor(&subProcedures); - - for (bool found = procAccessor.getFirst(); found; found = procAccessor.getNext()) + for (const auto& procPair : subProcedures) { - if (!procAccessor.current()->second->dsqlBlock) + if (!procPair.second->dsqlBlock) { status_exception::raise( Arg::Gds(isc_subproc_not_impl) << - procAccessor.current()->first.c_str()); + procPair.first.c_str()); } } } @@ -430,6 +424,31 @@ void DsqlCompilerScratch::putLocalVariable(dsql_var* variable, const DeclareVari ++hiddenVarsNumber; } +// Put maps in subroutines for outer variables/parameters usage. +void DsqlCompilerScratch::putOuterMaps() +{ + if (!outerMessagesMap.count() && !outerVarsMap.count()) + return; + + appendUChar(blr_outer_map); + + for (auto& pair : outerVarsMap) + { + appendUChar(blr_outer_map_variable); + appendUShort(pair.first); + appendUShort(pair.second); + } + + for (auto& pair : outerMessagesMap) + { + appendUChar(blr_outer_map_message); + appendUShort(pair.first); + appendUShort(pair.second); + } + + appendUChar(blr_end); +} + // Make a variable. dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name, const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber) diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index 632fbccb13..beb0bb1eb4 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -117,10 +117,12 @@ public: outputVariables(p), returningClause(nullptr), currCteAlias(NULL), + mainScratch(aMainScratch), + outerMessagesMap(p), + outerVarsMap(p), ctes(p), cteAliases(p), psql(false), - mainScratch(aMainScratch), subFunctions(p), subProcedures(p) { @@ -181,6 +183,7 @@ public: void putLocalVariables(CompoundStmtNode* parameters, USHORT locals); void putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam, const MetaName& collationName); + void putOuterMaps(); dsql_var* makeVariable(dsql_fld*, const char*, const dsql_var::Type type, USHORT, USHORT, USHORT); dsql_var* resolveVariable(const MetaName& varName); @@ -313,14 +316,16 @@ public: Firebird::Array outputVariables; ReturningClause* returningClause; const Firebird::string* const* currCteAlias; + DsqlCompilerScratch* mainScratch; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // private: Firebird::HalfStaticArray ctes; // common table expressions Firebird::HalfStaticArray cteAliases; // CTE aliases in recursive members bool psql; - DsqlCompilerScratch* mainScratch; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; }; class PsqlChanger diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 23bc600f0d..c26204b47a 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -9408,7 +9408,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) //-------------------- -static RegisterNode regParameterNode({blr_parameter, blr_parameter2, blr_parameter3}); +static RegisterNode regParameterNode({blr_parameter, blr_parameter2}); ParameterNode::ParameterNode(MemoryPool& pool) : TypedNode(pool) @@ -9417,27 +9417,28 @@ ParameterNode::ParameterNode(MemoryPool& pool) DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { - MessageNode* message = NULL; - USHORT n = csb->csb_blr_reader.getByte(); + MessageNode* message = nullptr; + const USHORT messageNum = csb->csb_blr_reader.getByte(); - if (n >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[n].csb_message)) + if (messageNum >= csb->csb_rpt.getCount() || !(message = csb->csb_rpt[messageNum].csb_message)) PAR_error(csb, Arg::Gds(isc_badmsgnum)); - ParameterNode* node = FB_NEW_POOL(pool) ParameterNode(pool); - + const auto node = FB_NEW_POOL(pool) ParameterNode(pool); node->message = message; node->argNumber = csb->csb_blr_reader.getWord(); + node->outerDecl = csb->outerMessagesMap.exist(messageNum); - const Format* format = message->format; + const auto format = message->format; if (node->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); if (blrOp != blr_parameter) { - ParameterNode* flagNode = FB_NEW_POOL(pool) ParameterNode(pool); + const auto flagNode = FB_NEW_POOL(pool) ParameterNode(pool); flagNode->message = message; flagNode->argNumber = csb->csb_blr_reader.getWord(); + flagNode->outerDecl = node->outerDecl; if (flagNode->argNumber >= format->fmt_count) PAR_error(csb, Arg::Gds(isc_badparnum)); @@ -9445,16 +9446,12 @@ DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScr node->argFlag = flagNode; } - if (blrOp == blr_parameter3) + if (node->outerDecl) { - ParameterNode* indicatorNode = FB_NEW_POOL(pool) ParameterNode(pool); - indicatorNode->message = message; - indicatorNode->argNumber = csb->csb_blr_reader.getWord(); + fb_assert(csb->mainCsb); - if (indicatorNode->argNumber >= format->fmt_count) - PAR_error(csb, Arg::Gds(isc_badparnum)); - - node->argIndicator = indicatorNode; + if (csb->mainCsb) + message->itemsUsedInSubroutines.add(node->argNumber); } return node; @@ -9469,8 +9466,8 @@ string ParameterNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, message); NODE_PRINT(printer, argNumber); NODE_PRINT(printer, argFlag); - NODE_PRINT(printer, argIndicator); NODE_PRINT(printer, argInfo); + NODE_PRINT(printer, outerDecl); return "ParameterNode"; } @@ -9490,6 +9487,7 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr); node->dsqlParameterIndex = dsqlParameterIndex; + node->outerDecl = outerDecl; return node; } @@ -9621,6 +9619,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch, void ParameterNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + fb_assert(!outerDecl); GEN_parameter(dsqlScratch, dsqlParameter); } @@ -9641,7 +9640,20 @@ bool ParameterNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* { const ParameterNode* o = nodeAs(other); - return o && dsqlParameter->par_index == o->dsqlParameter->par_index; + return o && outerDecl == o->outerDecl && dsqlParameter->par_index == o->dsqlParameter->par_index; +} + +jrd_req* ParameterNode::getParamRequest(jrd_req* request) const +{ + auto paramRequest = request; + + if (outerDecl) + { + while (paramRequest->getStatement()->parentStatement) + paramRequest = paramRequest->req_caller; + } + + return paramRequest; } void ParameterNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) @@ -9676,14 +9688,16 @@ ValueExprNode* ParameterNode::copy(thread_db* tdbb, NodeCopier& copier) const node->message = message; node->argFlag = copier.copy(tdbb, argFlag); - node->argIndicator = copier.copy(tdbb, argIndicator); + node->outerDecl = outerDecl; return node; } ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - argInfo = CMP_pass2_validation(tdbb, csb, + const auto paramCsb = outerDecl ? csb->mainCsb : csb; + + argInfo = CMP_pass2_validation(tdbb, paramCsb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber)); ValueExprNode::pass2(tdbb, csb); @@ -9691,8 +9705,8 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc desc; getDesc(tdbb, csb, &desc); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (message->itemsUsedInSubroutines.exist(argNumber)) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); @@ -9701,11 +9715,28 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const { - impure_value* const impure = request->getImpure(impureOffset); - request->req_flags &= ~req_null; + dsc* retDesc; + impure_value* impureForOuter; + if (message->itemsUsedInSubroutines.exist(argNumber)) + { + impureForOuter = request->getImpure(impureOffset); + retDesc = &impureForOuter->vlu_desc; + } + else + { + impureForOuter = nullptr; + retDesc = request->getImpure(impureOffset); + } + + const auto paramRequest = getParamRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); const dsc* desc; + request->req_flags &= ~req_null; + if (argFlag) { desc = EVL_expr(tdbb, request, argFlag); @@ -9715,32 +9746,37 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const desc = &message->format->fmt_desc[argNumber]; - impure->vlu_desc.dsc_address = request->getImpure( + retDesc->dsc_address = paramRequest->getImpure( message->impureOffset + (IPTR) desc->dsc_address); - impure->vlu_desc.dsc_dtype = desc->dsc_dtype; - impure->vlu_desc.dsc_length = desc->dsc_length; - impure->vlu_desc.dsc_scale = desc->dsc_scale; - impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; + retDesc->dsc_dtype = desc->dsc_dtype; + retDesc->dsc_length = desc->dsc_length; + retDesc->dsc_scale = desc->dsc_scale; + retDesc->dsc_sub_type = desc->dsc_sub_type; - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); + if (!(request->req_flags & req_null)) + { + if (impureForOuter) + EVL_make_value(tdbb, retDesc, impureForOuter); - USHORT* impure_flags = request->getImpure( + if (retDesc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, retDesc); + } + + auto impureFlags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * argNumber)); - if (!(*impure_flags & VLU_checked)) + if (!(*impureFlags & VLU_checked)) { if (!(request->req_flags & req_null)) { USHORT maxLen = desc->dsc_length; // not adjusted length - desc = &impure->vlu_desc; - if (DTYPE_IS_TEXT(desc->dsc_dtype)) + if (DTYPE_IS_TEXT(retDesc->dsc_dtype)) { - const UCHAR* p = desc->dsc_address; + const UCHAR* p = retDesc->dsc_address; USHORT len; - switch (desc->dsc_dtype) + switch (retDesc->dsc_dtype) { case dtype_cstring: len = strnlen((const char*) p, maxLen); @@ -9748,7 +9784,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; case dtype_text: - len = desc->dsc_length; + len = retDesc->dsc_length; break; case dtype_varying: @@ -9758,24 +9794,24 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const break; } - CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc)); + auto charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(retDesc)); EngineCallbacks::instance->validateData(charSet, len, p); - EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(desc), len, p, maxLen); + EngineCallbacks::instance->validateLength(charSet, DSC_GET_CHARSET(retDesc), len, p, maxLen); } - else if (desc->isBlob()) + else if (retDesc->isBlob()) { - const bid* const blobId = reinterpret_cast(desc->dsc_address); + const bid* const blobId = reinterpret_cast(retDesc->dsc_address); if (!blobId->isEmpty()) { if (!request->hasInternalStatement()) tdbb->getTransaction()->checkBlob(tdbb, blobId, NULL, false); - if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY) + if (retDesc->getCharSet() != CS_NONE && retDesc->getCharSet() != CS_BINARY) { AutoBlb blob(tdbb, blb::open(tdbb, tdbb->getTransaction(), blobId)); - blob.getBlb()->BLB_check_well_formed(tdbb, desc); + blob.getBlb()->BLB_check_well_formed(tdbb, retDesc); } } } @@ -9784,13 +9820,13 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const if (argInfo) { EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber), - argInfo, &impure->vlu_desc, request->req_flags & req_null); + argInfo, retDesc, request->req_flags & req_null); } - *impure_flags |= VLU_checked; + *impureFlags |= VLU_checked; } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : retDesc; } @@ -13464,24 +13500,25 @@ static RegisterNode regVariableNode({blr_variable}); VariableNode::VariableNode(MemoryPool& pool) : TypedNode(pool), - dsqlName(pool), - dsqlVar(NULL), - varDecl(NULL), - varInfo(NULL), - varId(0) + dsqlName(pool) { } -DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) +DmlNode* VariableNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { const USHORT n = csb->csb_blr_reader.getWord(); - vec* vector = csb->csb_variables; - - if (!vector || n >= vector->count()) - PAR_error(csb, Arg::Gds(isc_badvarnum)); VariableNode* node = FB_NEW_POOL(pool) VariableNode(pool); node->varId = n; + node->outerDecl = csb->outerVarsMap.exist(n); + + if (node->outerDecl) + { + fb_assert(csb->mainCsb); + + if (csb->mainCsb) + csb->mainCsb->csb_variables_used_in_subroutines.add(node->varId); + } return node; } @@ -13495,6 +13532,7 @@ string VariableNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, varId); NODE_PRINT(printer, varDecl); NODE_PRINT(printer, varInfo); + NODE_PRINT(printer, outerDecl); return "VariableNode"; } @@ -13505,6 +13543,35 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) node->dsqlName = dsqlName; node->dsqlVar = dsqlVar ? dsqlVar.getObject() : dsqlScratch->resolveVariable(dsqlName); + if (!node->dsqlVar && dsqlScratch->mainScratch) + { + if ((node->dsqlVar = dsqlScratch->mainScratch->resolveVariable(dsqlName))) + { + node->outerDecl = true; + + const bool execBlock = (dsqlScratch->mainScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(dsqlScratch->mainScratch->flags & + (DsqlCompilerScratch::FLAG_PROCEDURE | + DsqlCompilerScratch::FLAG_TRIGGER | + DsqlCompilerScratch::FLAG_FUNCTION)); + + if (node->dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) + { + if (!dsqlScratch->outerMessagesMap.exist(node->dsqlVar->msgNumber)) + { + // 0 = input, 1 = output. Start outer messages with 2. + dsqlScratch->outerMessagesMap.put( + node->dsqlVar->msgNumber, 2 + dsqlScratch->outerMessagesMap.count()); + } + } + else + { + if (!dsqlScratch->outerVarsMap.exist(node->dsqlVar->number)) + dsqlScratch->outerVarsMap.put(node->dsqlVar->number, dsqlScratch->hiddenVarsNumber++); + } + } + } + if (!node->dsqlVar) PASS1_field_unknown(NULL, dsqlName.c_str(), this); @@ -13518,8 +13585,10 @@ void VariableNode::setParameterName(dsql_par* parameter) const void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - bool execBlock = (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && - !(dsqlScratch->flags & + auto varScratch = outerDecl ? dsqlScratch->mainScratch : dsqlScratch; + + const bool execBlock = (varScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && + !(varScratch->flags & (DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION)); @@ -13527,7 +13596,16 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) { dsqlScratch->appendUChar(blr_parameter2); - dsqlScratch->appendUChar(dsqlVar->msgNumber); + + if (outerDecl) + { + const auto messageNumPtr = dsqlScratch->outerMessagesMap.get(dsqlVar->msgNumber); + fb_assert(messageNumPtr); + dsqlScratch->appendUChar(*messageNumPtr); + } + else + dsqlScratch->appendUChar(dsqlVar->msgNumber); + dsqlScratch->appendUShort(dsqlVar->msgItem); dsqlScratch->appendUShort(dsqlVar->msgItem + 1); } @@ -13535,7 +13613,15 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) { // If this is an EXECUTE BLOCK input parameter, use the internal variable. dsqlScratch->appendUChar(blr_variable); - dsqlScratch->appendUShort(dsqlVar->number); + + if (outerDecl) + { + const auto varNumPtr = dsqlScratch->outerVarsMap.get(dsqlVar->number); + fb_assert(varNumPtr); + dsqlScratch->appendUShort(*varNumPtr); + } + else + dsqlScratch->appendUShort(dsqlVar->number); } } @@ -13550,7 +13636,8 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o if (!o) return false; - if (dsqlVar->field != o->dsqlVar->field || + if (outerDecl != o->outerDecl || + dsqlVar->field != o->dsqlVar->field || dsqlVar->field->fld_name != o->dsqlVar->field->fld_name || dsqlVar->number != o->dsqlVar->number || dsqlVar->msgItem != o->dsqlVar->msgItem || @@ -13562,6 +13649,19 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o return true; } +jrd_req* VariableNode::getVarRequest(jrd_req* request) const +{ + auto varRequest = request; + + if (outerDecl) + { + while (varRequest->getStatement()->parentStatement) + varRequest = varRequest->req_caller; + } + + return varRequest; +} + void VariableNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc) { *desc = varDecl->varDesc; @@ -13571,6 +13671,7 @@ ValueExprNode* VariableNode::copy(thread_db* tdbb, NodeCopier& copier) const { VariableNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool()); node->varId = copier.csb->csb_remap_variable + varId; + node->outerDecl = outerDecl; node->varDecl = varDecl; node->varInfo = varInfo; @@ -13591,12 +13692,14 @@ ValueExprNode* VariableNode::pass1(thread_db* tdbb, CompilerScratch* csb) ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - varInfo = CMP_pass2_validation(tdbb, csb, Item(Item::TYPE_VARIABLE, varId)); + const auto varCsb = outerDecl ? csb->mainCsb : csb; + + varInfo = CMP_pass2_validation(tdbb, varCsb, Item(Item::TYPE_VARIABLE, varDecl->varId)); ValueExprNode::pass2(tdbb, csb); - if (nodFlags & FLAG_VALUE) - impureOffset = csb->allocImpure(); + if (varDecl->usedInSubRoutines) + impureOffset = csb->allocImpure(); else impureOffset = csb->allocImpure(); @@ -13605,31 +13708,68 @@ ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb) dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const { - impure_value* const impure = request->getImpure(impureOffset); - impure_value* impure2 = request->getImpure(varDecl->impureOffset); + const auto varRequest = getVarRequest(request); + const auto varImpure = varRequest->getImpure(varDecl->impureOffset); request->req_flags &= ~req_null; - if (impure2->vlu_desc.dsc_flags & DSC_null) - request->req_flags |= req_null; + dsc* desc; - impure->vlu_desc = impure2->vlu_desc; - - if (impure->vlu_desc.dsc_dtype == dtype_text) - INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); - - if (!(impure2->vlu_flags & VLU_checked)) + if (varDecl->usedInSubRoutines) { - if (varInfo) + const auto impure = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + else { - EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, - &impure->vlu_desc, (impure->vlu_desc.dsc_flags & DSC_null)); + EVL_make_value(tdbb, &varImpure->vlu_desc, impure); + + if (impure->vlu_desc.dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); } - impure2->vlu_flags |= VLU_checked; + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + &impure->vlu_desc, (varImpure->vlu_desc.dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } + + desc = &impure->vlu_desc; + } + else + { + desc = request->getImpure(impureOffset); + + if (varImpure->vlu_desc.dsc_flags & DSC_null) + request->req_flags |= req_null; + + *desc = varImpure->vlu_desc; + + if (desc->dsc_dtype == dtype_text) + INTL_adjust_text_descriptor(tdbb, desc); + + if (!(varImpure->vlu_flags & VLU_checked)) + { + if (varInfo) + { + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, + desc, (desc->dsc_flags & DSC_null)); + } + + varImpure->vlu_flags |= VLU_checked; + } } - return (request->req_flags & req_null) ? NULL : &impure->vlu_desc; + return (request->req_flags & req_null) ? nullptr : desc; } diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index b3d2b13212..2e9fe25a66 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -1579,10 +1579,7 @@ public: ValueExprNode::getChildren(holder, dsql); if (!dsql) - { holder.add(argFlag); - holder.add(argIndicator); - } } virtual Firebird::string internalPrint(NodePrinter& printer) const; @@ -1598,6 +1595,8 @@ public: virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; + jrd_req* getParamRequest(jrd_req* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); @@ -1608,10 +1607,10 @@ public: dsql_par* dsqlParameter = nullptr; NestConst message; NestConst argFlag; - NestConst argIndicator; NestConst argInfo; USHORT dsqlParameterIndex = 0; USHORT argNumber = 0; + bool outerDecl = false; }; @@ -2201,6 +2200,8 @@ public: dsqlDesc = desc; } + jrd_req* getVarRequest(jrd_req* request) const; + virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); @@ -2212,7 +2213,8 @@ public: NestConst dsqlVar; NestConst varDecl; NestConst varInfo; - USHORT varId; + USHORT varId = 0; + bool outerDecl = false; }; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 2ba6afaa41..fbc0277ef8 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -539,8 +539,7 @@ public: static const USHORT FLAG_DOUBLE = 0x20; static const USHORT FLAG_DATE = 0x40; static const USHORT FLAG_DECFLOAT = 0x80; - static const USHORT FLAG_VALUE = 0x100; // Full value area required in impure space. - static const USHORT FLAG_INT128 = 0x200; + static const USHORT FLAG_INT128 = 0x100; explicit ExprNode(Type aType, MemoryPool& pool) : DmlNode(pool), @@ -1409,6 +1408,7 @@ public: TYPE_MERGE_SEND, TYPE_MESSAGE, TYPE_MODIFY, + TYPE_OUTER_MAP, TYPE_POST_EVENT, TYPE_RECEIVE, TYPE_RETURN, diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index df013bae04..1919a984e7 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -1767,19 +1767,16 @@ void DeclareSubFuncNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +DeclareSubFuncNode* DeclareSubFuncNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { + ContextPoolHolder context(tdbb, &subCsb->csb_pool); + PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); + return this; } -DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) +DeclareSubFuncNode* DeclareSubFuncNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) { - // scope needed here? - { // scope - ContextPoolHolder context(tdbb, &subCsb->csb_pool); - PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); - } - return this; } @@ -2111,12 +2108,7 @@ void DeclareSubProcNode::genParameters(DsqlCompilerScratch* dsqlScratch, } } -DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) -{ - return this; -} - -DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/) +DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/) { ContextPoolHolder context(tdbb, &subCsb->csb_pool); PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); @@ -2124,6 +2116,11 @@ DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* return this; } +DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) +{ + return this; +} + const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const { // Nothing to execute. This is the declaration node. @@ -2207,6 +2204,9 @@ DeclareVariableNode* DeclareVariableNode::pass1(thread_db* tdbb, CompilerScratch fb_assert(!(*vector)[varId]); (*vector)[varId] = this; + if (!csb->mainCsb && csb->csb_variables_used_in_subroutines.exist(varId)) + usedInSubRoutines = true; + return this; } @@ -4490,6 +4490,8 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->loopLevel = 0; StmtNode* stmtNode = body->dsqlPass(dsqlScratch); + + dsqlScratch->putOuterMaps(); GEN_hidden_variables(dsqlScratch); dsqlScratch->appendUChar(blr_stall); @@ -7133,6 +7135,104 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg //-------------------- +static RegisterNode regOuterMapNode({blr_outer_map}); + +DmlNode* OuterMapNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) +{ + fb_assert(csb->mainCsb); + if (!csb->mainCsb) + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map. Must be inside subroutine."); + + const auto node = FB_NEW_POOL(pool) OuterMapNode(pool); + + auto& blrReader = csb->csb_blr_reader; + UCHAR subCode; + + while ((subCode = blrReader.getByte()) != blr_end) + { + switch (subCode) + { + case blr_outer_map_message: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->outerMessagesMap.put(innerNumber, outerNumber); + + const auto outerMessage = CMP_csb_element(csb->mainCsb, outerNumber)->csb_message; + if (!outerMessage) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: outer message does not exist"); + } + + const auto tail = CMP_csb_element(csb, innerNumber); + if (tail->csb_message) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_message: inner message already exist"); + } + + tail->csb_message = outerMessage; + + if (innerNumber > csb->csb_msg_number) + csb->csb_msg_number = innerNumber; + + break; + } + + case blr_outer_map_variable: + { + const USHORT outerNumber = blrReader.getWord(); + const USHORT innerNumber = blrReader.getWord(); + + csb->mainCsb->csb_variables_used_in_subroutines.add(outerNumber); + csb->outerVarsMap.put(innerNumber, outerNumber); + + auto& outerVariables = *csb->mainCsb->csb_variables; + if (outerNumber >= outerVariables.count() || !outerVariables[outerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: outer variable does not exist"); + } + + auto& innerVariables = *(csb->csb_variables = vec::newVector( + *tdbb->getDefaultPool(), csb->csb_variables, innerNumber + 1)); + + if (innerVariables[innerNumber]) + { + fb_assert(false); + PAR_error(csb, Arg::Gds(isc_random) << + "Invalid blr_outer_map_variable: inner variable already exist"); + } + + innerVariables[innerNumber] = outerVariables[outerNumber]; + break; + } + + default: + PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_outer_map sub code"); + } + } + + return node; +} + +const StmtNode* OuterMapNode::execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const +{ + if (request->req_operation == jrd_req::req_evaluate) + request->req_operation = jrd_req::req_return; + + return parentStmt; +} + + +//-------------------- + + static RegisterNode regPostEventNode({blr_post, blr_post_arg}); DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index c9fe09b141..d8fd591ca8 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -477,9 +477,7 @@ class DeclareVariableNode final : public TypedNode(pool), - dsqlDef(NULL), - varId(0) + : TypedNode(pool) { varDesc.clear(); } @@ -498,7 +496,8 @@ public: public: NestConst dsqlDef; dsc varDesc; - USHORT varId; + USHORT varId = 0; + bool usedInSubRoutines = false; }; @@ -1093,9 +1092,7 @@ class MessageNode : public TypedNode public: explicit MessageNode(MemoryPool& pool) : TypedNode(pool), - format(NULL), - impureFlags(0), - messageNumber(0) + itemsUsedInSubroutines(pool) { } @@ -1116,9 +1113,10 @@ public: virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; public: + Firebird::SortedArray itemsUsedInSubroutines; NestConst format; - ULONG impureFlags; - USHORT messageNumber; + ULONG impureFlags = 0; + USHORT messageNumber = 0; }; @@ -1171,6 +1169,40 @@ public: }; +class OuterMapNode final : public TypedNode +{ +public: + explicit OuterMapNode(MemoryPool& pool) + : TypedNode(pool) + { + } + +public: + static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + + Firebird::string internalPrint(NodePrinter& /*printer*/) const override + { + return "OuterMapNode"; + } + + void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) override + { + } + + OuterMapNode* pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + OuterMapNode* pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) override + { + return this; + } + + const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const override; +}; + + class PostEventNode final : public TypedNode { public: diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index e0dc3944a9..d8bbb41689 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -77,14 +77,9 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch) * Emit BLR for hidden variables. * **************************************/ - if (dsqlScratch->hiddenVariables.isEmpty()) - return; - for (Array::const_iterator i = dsqlScratch->hiddenVariables.begin(); - i != dsqlScratch->hiddenVariables.end(); - ++i) + for (const auto var : dsqlScratch->hiddenVariables) { - const dsql_var* var = *i; dsqlScratch->appendUChar(blr_dcl_variable); dsqlScratch->appendUShort(var->number); GEN_descriptor(dsqlScratch, &var->desc, true); @@ -247,6 +242,7 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node) if (!block) scratch->appendUChar(blr_begin); + scratch->putOuterMaps(); GEN_hidden_variables(scratch); switch (statement->getType()) diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index 9b61310e0c..3ad0d2a650 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -193,8 +193,8 @@ #define blr_agg_min (unsigned char)85 #define blr_agg_total (unsigned char)86 #define blr_agg_average (unsigned char)87 -#define blr_parameter3 (unsigned char)88 /* same as Rdb definition */ /* unsupported +#define blr_parameter3 (unsigned char)88 #define blr_run_max (unsigned char)89 #define blr_run_min (unsigned char)90 #define blr_run_total (unsigned char)91 @@ -221,7 +221,7 @@ // unused codes: 111..117 -#define blr_run_count (unsigned char)118 /* changed from 88 to avoid conflict with blr_parameter3 */ +///#define blr_run_count (unsigned char)118 #define blr_rs_stream (unsigned char)119 #define blr_exec_proc (unsigned char)120 @@ -452,4 +452,8 @@ #define blr_local_table_truncate (unsigned char) 219 #define blr_local_table_id (unsigned char) 220 +#define blr_outer_map (unsigned char) 221 +#define blr_outer_map_message (unsigned char) 1 +#define blr_outer_map_variable (unsigned char) 2 + #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 9605f06563..7e46554621 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -136,9 +136,6 @@ SortNode* SortNode::pass1(thread_db* tdbb, CompilerScratch* csb) SortNode* SortNode::pass2(thread_db* tdbb, CompilerScratch* csb) { - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) - (*i)->nodFlags |= ExprNode::FLAG_VALUE; - for (NestConst* i = expressions.begin(); i != expressions.end(); ++i) ExprNode::doPass2(tdbb, csb, i->getAddress()); diff --git a/src/jrd/blp.h b/src/jrd/blp.h index b6ed5eacee..b4ee1dd7a2 100644 --- a/src/jrd/blp.h +++ b/src/jrd/blp.h @@ -252,5 +252,6 @@ static const struct {"dcl_local_table", dcl_local_table}, {"local_table_truncate", one_word}, {"local_table_id", local_table}, + {"outer_map", outer_map}, {0, 0} }; diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 5d3aa24b54..5cefa6e919 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -136,25 +136,18 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) DEV_BLKCHK(node, type_nod); jrd_req* request = tdbb->getRequest(); - impure_value* impure = request->getImpure(node->impureOffset); // The only nodes that can be assigned to are: argument, field and variable. - int arg_number; - const dsc* desc; - const MessageNode* message; - Record* record; - const ParameterNode* paramNode; - const VariableNode* varNode; - const FieldNode* fieldNode; - - if ((paramNode = nodeAs(node))) + if (auto paramNode = nodeAs(node)) { - message = paramNode->message; - arg_number = paramNode->argNumber; - desc = &message->format->fmt_desc[arg_number]; + auto message = paramNode->message; + auto arg_number = paramNode->argNumber; + auto desc = &message->format->fmt_desc[arg_number]; - impure->vlu_desc.dsc_address = request->getImpure( + auto impure = request->getImpure(node->impureOffset); + + impure->vlu_desc.dsc_address = paramNode->getParamRequest(request)->getImpure( message->impureOffset + (IPTR) desc->dsc_address); impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_length = desc->dsc_length; @@ -177,15 +170,15 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) } else if (nodeIs(node)) return NULL; - else if ((varNode = nodeAs(node))) + else if (auto varNode = nodeAs(node)) { - // Calculate descriptor - impure = request->getImpure(varNode->varDecl->impureOffset); + auto impure = varNode->getVarRequest(request)->getImpure(varNode->varDecl->impureOffset); return &impure->vlu_desc; } - else if ((fieldNode = nodeAs(node))) + else if (auto fieldNode = nodeAs(node)) { - record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto record = request->req_rpb[fieldNode->fieldStream].rpb_record; + auto impure = request->getImpure(node->impureOffset); if (!EVL_field(0, record, fieldNode->fieldId, &impure->vlu_desc)) { @@ -672,11 +665,10 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d request->req_flags = flags; } - Firebird::string s; - if (err) { ISC_STATUS status = isc_not_valid_for_var; + string s; const char* arg; if (item.type == Item::TYPE_CAST) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 8425597760..499950d0e7 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -261,6 +261,14 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo SET_TDBB(tdbb); jrd_req* request = tdbb->getRequest(); + const auto toVar = nodeAs(to); + + if (toVar && toVar->outerDecl) + request = toVar->getVarRequest(request); + + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); + // Get descriptors of receiving and sending fields/parameters, variables, etc. dsc* missing = NULL; @@ -284,31 +292,39 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo null = -1; USHORT* impure_flags = NULL; - const ParameterNode* toParam; - const VariableNode* toVar; + const auto toParam = nodeAs(to); - if ((toParam = nodeAs(to))) + if (toParam) { const MessageNode* message = toParam->message; + const auto paramRequest = toParam->getParamRequest(request); if (toParam->argInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest); + EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber), toParam->argInfo, from_desc, null == -1); } - impure_flags = request->getImpure( + impure_flags = paramRequest->getImpure( message->impureFlags + (sizeof(USHORT) * toParam->argNumber)); } - else if ((toVar = nodeAs(to))) + else if (toVar) { + const auto varRequest = toVar->getVarRequest(request); + if (toVar->varInfo) { + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest); + EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId), toVar->varInfo, from_desc, null == -1); } - impure_flags = &request->getImpure( + impure_flags = &varRequest->getImpure( toVar->varDecl->impureOffset)->vlu_flags; } @@ -321,43 +337,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo if (!null) { - // if necessary and appropriate, use the indicator variable - - if (toParam && toParam->argIndicator) - { - dsc* indicator = EVL_assign_to(tdbb, toParam->argIndicator); - temp.dsc_dtype = dtype_short; - temp.dsc_length = sizeof(SSHORT); - temp.dsc_scale = 0; - temp.dsc_sub_type = 0; - - SSHORT len; - - if ((from_desc->dsc_dtype <= dtype_varying) && (to_desc->dsc_dtype <= dtype_varying) && - (TEXT_LEN(from_desc) > TEXT_LEN(to_desc))) - { - len = TEXT_LEN(from_desc); - } - else - len = 0; - - temp.dsc_address = (UCHAR *) &len; - MOV_move(tdbb, &temp, indicator); - - if (len) - { - temp = *from_desc; - temp.dsc_length = TEXT_LEN(to_desc); - - if (temp.dsc_dtype == dtype_cstring) - temp.dsc_length += 1; - else if (temp.dsc_dtype == dtype_varying) - temp.dsc_length += 2; - - from_desc = &temp; - } - } - // Validate range for datetime values if (DTYPE_IS_DATE(from_desc->dsc_dtype)) @@ -454,7 +433,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo // Handle the null flag as appropriate for fields and message arguments. - const FieldNode* toField = nodeAs(to); if (toField) { @@ -499,12 +477,6 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo temp.dsc_sub_type = 0; temp.dsc_address = (UCHAR*) &null; MOV_move(tdbb, &temp, to_desc); - - if (null && toParam->argIndicator) - { - to_desc = EVL_assign_to(tdbb, toParam->argIndicator); - MOV_move(tdbb, &temp, to_desc); - } } } diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 0d04522a4d..3a0bd26a25 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -36,6 +36,7 @@ #include "../jrd/Relation.h" #include "../common/classes/array.h" #include "../jrd/MetaName.h" +#include "../common/classes/fb_pair.h" #include "../common/classes/NestConst.h" #include "iberror.h" @@ -453,12 +454,15 @@ public: csb_current_nodes(p), csb_current_for_nodes(p), csb_computing_fields(p), + csb_variables_used_in_subroutines(p), csb_pool(p), csb_map_field_info(p), csb_map_item_info(p), csb_message_pad(p), subFunctions(p), subProcedures(p), + outerMessagesMap(p), + outerVarsMap(p), csb_currentForNode(NULL), csb_currentDMLNode(NULL), csb_currentAssignTarget(NULL), @@ -517,6 +521,7 @@ public: // candidates within whose scope we are Firebird::Array csb_current_for_nodes; Firebird::SortedArray csb_computing_fields; // Computed fields being compiled + Firebird::SortedArray csb_variables_used_in_subroutines; StreamType csb_n_stream; // Next available stream USHORT csb_msg_number; // Highest used message number ULONG csb_impure; // Next offset into impure area @@ -541,8 +546,10 @@ public: bool csb_returning_expr; bool csb_implicit_cursor; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + Firebird::LeftPooledMap subFunctions; + Firebird::LeftPooledMap subProcedures; + Firebird::NonPooledMap outerMessagesMap; // + Firebird::NonPooledMap outerVarsMap; // ForNode* csb_currentForNode; StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 65513746ea..3c958d9627 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -1141,6 +1141,7 @@ void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedu MemoryPool& pool = *tdbb->getDefaultPool(); // We have a few parameters. Get on with creating the message block + // Outer messages map may start with 2, but they are always in the routine start. USHORT n = ++csb->csb_msg_number; if (n < 2) csb->csb_msg_number = n = 2; diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 618ddeccb7..c0b4f3143d 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -318,6 +318,7 @@ const int op_subfunc_decl = 28; const int op_window_win = 29; const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_dcl_local_table = 31; +const int op_outer_map = 32; static const UCHAR // generic print formats @@ -406,7 +407,8 @@ static const UCHAR store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0}, erase[] = { op_erase, 0}, - local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}; + local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}, + outer_map[] = { op_outer_map, 0 }; #include "../jrd/blp.h" @@ -3885,6 +3887,46 @@ static void blr_print_verb(gds_ctl* control, SSHORT level) break; } + case op_outer_map: + { + offset = blr_print_line(control, offset); + + static const char* subCodes[] = + { + nullptr, + "message", + "variable" + }; + + while ((blr_operator = control->ctl_blr_reader.getByte()) != blr_end) + { + blr_indent(control, level); + + if (blr_operator == 0 || blr_operator >= FB_NELEM(subCodes)) + blr_error(control, "*** invalid blr_outer_map sub code ***"); + + blr_format(control, "blr_outer_map_%s, ", subCodes[blr_operator]); + + switch (blr_operator) + { + case blr_outer_map_message: + case blr_outer_map_variable: + blr_print_word(control); + n = blr_print_word(control); + offset = blr_print_line(control, offset); + break; + + default: + fb_assert(false); + } + } + + // print blr_end + control->ctl_blr_reader.seekBackward(1); + blr_print_verb(control, level); + break; + } + default: fb_assert(false); break;