8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 17:23:03 +01:00

Improvement #4769 - Allow sub-routines to access variables/parameters

defined at the outer/parent level [CORE4449].

Remove blr_parameter3.

Comment unused blr_run_count.
This commit is contained in:
Adriano dos Santos Fernandes 2022-01-30 12:11:05 -03:00
parent e443f02dd3
commit 3c22c23874
18 changed files with 533 additions and 214 deletions

View File

@ -11,7 +11,7 @@ Description:
Syntax: Syntax:
<declaration item> ::= <declaration item> ::=
DECLARE [VARIABLE] <variable name> <data type> [ := <value> ]; DECLARE [VARIABLE] <variable name> <data type> [ = <value> ];
| |
DECLARE [VARIABLE] CURSOR <cursor name> FOR (<query>); DECLARE [VARIABLE] CURSOR <cursor name> FOR (<query>);
| |
@ -46,8 +46,11 @@ Syntax:
Limitations: Limitations:
1) Subroutines may not be nested in another subroutine. They are only supported in the main 1) Subroutines may not be nested in another subroutine. They are only supported in the main
routine. routine.
2) Currently, a subroutine may not directly access or use variables or cursors of the 2) Currently, a subroutine may not directly access cursors of the main routine/block.
main statements. This may be allowed in the future. 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: Notes:
1) Starting in FB 4, subroutines may be recursive or call others subroutines. 1) Starting in FB 4, subroutines may be recursive or call others subroutines.

View File

@ -2342,6 +2342,8 @@ void CreateAlterFunctionNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch*
dsqlScratch->cursorNumber = 0; dsqlScratch->cursorNumber = 0;
StmtNode* stmtNode = body->dsqlPass(dsqlScratch); StmtNode* stmtNode = body->dsqlPass(dsqlScratch);
dsqlScratch->putOuterMaps();
GEN_hidden_variables(dsqlScratch); GEN_hidden_variables(dsqlScratch);
dsqlScratch->appendUChar(blr_stall); dsqlScratch->appendUChar(blr_stall);
@ -3235,6 +3237,8 @@ void CreateAlterProcedureNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch*
dsqlScratch->cursorNumber = 0; dsqlScratch->cursorNumber = 0;
StmtNode* stmtNode = body->dsqlPass(dsqlScratch); StmtNode* stmtNode = body->dsqlPass(dsqlScratch);
dsqlScratch->putOuterMaps();
GEN_hidden_variables(dsqlScratch); GEN_hidden_variables(dsqlScratch);
dsqlScratch->appendUChar(blr_stall); dsqlScratch->appendUChar(blr_stall);
@ -3751,6 +3755,8 @@ void CreateAlterTriggerNode::compile(thread_db* /*tdbb*/, DsqlCompilerScratch* d
dsqlScratch->scopeLevel++; dsqlScratch->scopeLevel++;
StmtNode* stmtNode = body->dsqlPass(dsqlScratch); StmtNode* stmtNode = body->dsqlPass(dsqlScratch);
dsqlScratch->putOuterMaps();
GEN_hidden_variables(dsqlScratch); GEN_hidden_variables(dsqlScratch);
// dimitr: I see no reason to deny EXIT command in triggers, // dimitr: I see no reason to deny EXIT command in triggers,

View File

@ -345,30 +345,24 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT
if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE)) if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE))
{ {
// Check not implemented sub-functions. // Check not implemented sub-functions.
for (const auto& funcPair : subFunctions)
GenericMap<Left<MetaName, DeclareSubFuncNode*> >::ConstAccessor funcAccessor(&subFunctions);
for (bool found = funcAccessor.getFirst(); found; found = funcAccessor.getNext())
{ {
if (!funcAccessor.current()->second->dsqlBlock) if (!funcPair.second->dsqlBlock)
{ {
status_exception::raise( status_exception::raise(
Arg::Gds(isc_subfunc_not_impl) << Arg::Gds(isc_subfunc_not_impl) <<
funcAccessor.current()->first.c_str()); funcPair.first.c_str());
} }
} }
// Check not implemented sub-procedures. // Check not implemented sub-procedures.
for (const auto& procPair : subProcedures)
GenericMap<Left<MetaName, DeclareSubProcNode*> >::ConstAccessor procAccessor(&subProcedures);
for (bool found = procAccessor.getFirst(); found; found = procAccessor.getNext())
{ {
if (!procAccessor.current()->second->dsqlBlock) if (!procPair.second->dsqlBlock)
{ {
status_exception::raise( status_exception::raise(
Arg::Gds(isc_subproc_not_impl) << 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; ++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. // Make a variable.
dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name, dsql_var* DsqlCompilerScratch::makeVariable(dsql_fld* field, const char* name,
const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber) const dsql_var::Type type, USHORT msgNumber, USHORT itemNumber, USHORT localNumber)

View File

@ -117,10 +117,12 @@ public:
outputVariables(p), outputVariables(p),
returningClause(nullptr), returningClause(nullptr),
currCteAlias(NULL), currCteAlias(NULL),
mainScratch(aMainScratch),
outerMessagesMap(p),
outerVarsMap(p),
ctes(p), ctes(p),
cteAliases(p), cteAliases(p),
psql(false), psql(false),
mainScratch(aMainScratch),
subFunctions(p), subFunctions(p),
subProcedures(p) subProcedures(p)
{ {
@ -181,6 +183,7 @@ public:
void putLocalVariables(CompoundStmtNode* parameters, USHORT locals); void putLocalVariables(CompoundStmtNode* parameters, USHORT locals);
void putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam, void putLocalVariable(dsql_var* variable, const DeclareVariableNode* hostParam,
const MetaName& collationName); const MetaName& collationName);
void putOuterMaps();
dsql_var* makeVariable(dsql_fld*, const char*, const dsql_var::Type type, USHORT, dsql_var* makeVariable(dsql_fld*, const char*, const dsql_var::Type type, USHORT,
USHORT, USHORT); USHORT, USHORT);
dsql_var* resolveVariable(const MetaName& varName); dsql_var* resolveVariable(const MetaName& varName);
@ -313,14 +316,16 @@ public:
Firebird::Array<dsql_var*> outputVariables; Firebird::Array<dsql_var*> outputVariables;
ReturningClause* returningClause; ReturningClause* returningClause;
const Firebird::string* const* currCteAlias; const Firebird::string* const* currCteAlias;
DsqlCompilerScratch* mainScratch;
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <outer, inner>
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <outer, inner>
private: private:
Firebird::HalfStaticArray<SelectExprNode*, 4> ctes; // common table expressions Firebird::HalfStaticArray<SelectExprNode*, 4> ctes; // common table expressions
Firebird::HalfStaticArray<const Firebird::string*, 4> cteAliases; // CTE aliases in recursive members Firebird::HalfStaticArray<const Firebird::string*, 4> cteAliases; // CTE aliases in recursive members
bool psql; bool psql;
DsqlCompilerScratch* mainScratch; Firebird::LeftPooledMap<MetaName, DeclareSubFuncNode*> subFunctions;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubFuncNode*> > subFunctions; Firebird::LeftPooledMap<MetaName, DeclareSubProcNode*> subProcedures;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubProcNode*> > subProcedures;
}; };
class PsqlChanger class PsqlChanger

View File

@ -9408,7 +9408,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
//-------------------- //--------------------
static RegisterNode<ParameterNode> regParameterNode({blr_parameter, blr_parameter2, blr_parameter3}); static RegisterNode<ParameterNode> regParameterNode({blr_parameter, blr_parameter2});
ParameterNode::ParameterNode(MemoryPool& pool) ParameterNode::ParameterNode(MemoryPool& pool)
: TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool) : TypedNode<ValueExprNode, ExprNode::TYPE_PARAMETER>(pool)
@ -9417,27 +9417,28 @@ ParameterNode::ParameterNode(MemoryPool& pool)
DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
{ {
MessageNode* message = NULL; MessageNode* message = nullptr;
USHORT n = csb->csb_blr_reader.getByte(); 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)); 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->message = message;
node->argNumber = csb->csb_blr_reader.getWord(); 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) if (node->argNumber >= format->fmt_count)
PAR_error(csb, Arg::Gds(isc_badparnum)); PAR_error(csb, Arg::Gds(isc_badparnum));
if (blrOp != blr_parameter) 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->message = message;
flagNode->argNumber = csb->csb_blr_reader.getWord(); flagNode->argNumber = csb->csb_blr_reader.getWord();
flagNode->outerDecl = node->outerDecl;
if (flagNode->argNumber >= format->fmt_count) if (flagNode->argNumber >= format->fmt_count)
PAR_error(csb, Arg::Gds(isc_badparnum)); PAR_error(csb, Arg::Gds(isc_badparnum));
@ -9445,16 +9446,12 @@ DmlNode* ParameterNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScr
node->argFlag = flagNode; node->argFlag = flagNode;
} }
if (blrOp == blr_parameter3) if (node->outerDecl)
{ {
ParameterNode* indicatorNode = FB_NEW_POOL(pool) ParameterNode(pool); fb_assert(csb->mainCsb);
indicatorNode->message = message;
indicatorNode->argNumber = csb->csb_blr_reader.getWord();
if (indicatorNode->argNumber >= format->fmt_count) if (csb->mainCsb)
PAR_error(csb, Arg::Gds(isc_badparnum)); message->itemsUsedInSubroutines.add(node->argNumber);
node->argIndicator = indicatorNode;
} }
return node; return node;
@ -9469,8 +9466,8 @@ string ParameterNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, message); NODE_PRINT(printer, message);
NODE_PRINT(printer, argNumber); NODE_PRINT(printer, argNumber);
NODE_PRINT(printer, argFlag); NODE_PRINT(printer, argFlag);
NODE_PRINT(printer, argIndicator);
NODE_PRINT(printer, argInfo); NODE_PRINT(printer, argInfo);
NODE_PRINT(printer, outerDecl);
return "ParameterNode"; return "ParameterNode";
} }
@ -9490,6 +9487,7 @@ ValueExprNode* ParameterNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool()); auto node = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr); node->dsqlParameter = MAKE_parameter(msg, true, true, dsqlParameterIndex, nullptr);
node->dsqlParameterIndex = dsqlParameterIndex; node->dsqlParameterIndex = dsqlParameterIndex;
node->outerDecl = outerDecl;
return node; return node;
} }
@ -9621,6 +9619,7 @@ bool ParameterNode::setParameterType(DsqlCompilerScratch* dsqlScratch,
void ParameterNode::genBlr(DsqlCompilerScratch* dsqlScratch) void ParameterNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
fb_assert(!outerDecl);
GEN_parameter(dsqlScratch, dsqlParameter); GEN_parameter(dsqlScratch, dsqlParameter);
} }
@ -9641,7 +9640,20 @@ bool ParameterNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode*
{ {
const ParameterNode* o = nodeAs<ParameterNode>(other); const ParameterNode* o = nodeAs<ParameterNode>(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) 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->message = message;
node->argFlag = copier.copy(tdbb, argFlag); node->argFlag = copier.copy(tdbb, argFlag);
node->argIndicator = copier.copy(tdbb, argIndicator); node->outerDecl = outerDecl;
return node; return node;
} }
ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb) 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)); Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber));
ValueExprNode::pass2(tdbb, csb); ValueExprNode::pass2(tdbb, csb);
@ -9691,8 +9705,8 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb)
dsc desc; dsc desc;
getDesc(tdbb, csb, &desc); getDesc(tdbb, csb, &desc);
if (nodFlags & FLAG_VALUE) if (message->itemsUsedInSubroutines.exist(argNumber))
impureOffset = csb->allocImpure<impure_value_ex>(); impureOffset = csb->allocImpure<impure_value>();
else else
impureOffset = csb->allocImpure<dsc>(); impureOffset = csb->allocImpure<dsc>();
@ -9701,11 +9715,28 @@ ValueExprNode* ParameterNode::pass2(thread_db* tdbb, CompilerScratch* csb)
dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const
{ {
impure_value* const impure = request->getImpure<impure_value>(impureOffset); dsc* retDesc;
request->req_flags &= ~req_null; impure_value* impureForOuter;
if (message->itemsUsedInSubroutines.exist(argNumber))
{
impureForOuter = request->getImpure<impure_value>(impureOffset);
retDesc = &impureForOuter->vlu_desc;
}
else
{
impureForOuter = nullptr;
retDesc = request->getImpure<dsc>(impureOffset);
}
const auto paramRequest = getParamRequest(request);
AutoSetRestore2<jrd_req*, thread_db> autoSetRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest);
const dsc* desc; const dsc* desc;
request->req_flags &= ~req_null;
if (argFlag) if (argFlag)
{ {
desc = EVL_expr(tdbb, request, 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]; desc = &message->format->fmt_desc[argNumber];
impure->vlu_desc.dsc_address = request->getImpure<UCHAR>( retDesc->dsc_address = paramRequest->getImpure<UCHAR>(
message->impureOffset + (IPTR) desc->dsc_address); message->impureOffset + (IPTR) desc->dsc_address);
impure->vlu_desc.dsc_dtype = desc->dsc_dtype; retDesc->dsc_dtype = desc->dsc_dtype;
impure->vlu_desc.dsc_length = desc->dsc_length; retDesc->dsc_length = desc->dsc_length;
impure->vlu_desc.dsc_scale = desc->dsc_scale; retDesc->dsc_scale = desc->dsc_scale;
impure->vlu_desc.dsc_sub_type = desc->dsc_sub_type; retDesc->dsc_sub_type = desc->dsc_sub_type;
if (impure->vlu_desc.dsc_dtype == dtype_text) if (!(request->req_flags & req_null))
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc); {
if (impureForOuter)
EVL_make_value(tdbb, retDesc, impureForOuter);
USHORT* impure_flags = request->getImpure<USHORT>( if (retDesc->dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, retDesc);
}
auto impureFlags = paramRequest->getImpure<USHORT>(
message->impureFlags + (sizeof(USHORT) * argNumber)); message->impureFlags + (sizeof(USHORT) * argNumber));
if (!(*impure_flags & VLU_checked)) if (!(*impureFlags & VLU_checked))
{ {
if (!(request->req_flags & req_null)) if (!(request->req_flags & req_null))
{ {
USHORT maxLen = desc->dsc_length; // not adjusted length 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; USHORT len;
switch (desc->dsc_dtype) switch (retDesc->dsc_dtype)
{ {
case dtype_cstring: case dtype_cstring:
len = strnlen((const char*) p, maxLen); len = strnlen((const char*) p, maxLen);
@ -9748,7 +9784,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const
break; break;
case dtype_text: case dtype_text:
len = desc->dsc_length; len = retDesc->dsc_length;
break; break;
case dtype_varying: case dtype_varying:
@ -9758,24 +9794,24 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const
break; 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->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<bid*>(desc->dsc_address); const bid* const blobId = reinterpret_cast<bid*>(retDesc->dsc_address);
if (!blobId->isEmpty()) if (!blobId->isEmpty())
{ {
if (!request->hasInternalStatement()) if (!request->hasInternalStatement())
tdbb->getTransaction()->checkBlob(tdbb, blobId, NULL, false); 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)); 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) if (argInfo)
{ {
EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, argNumber), 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<VariableNode> regVariableNode({blr_variable});
VariableNode::VariableNode(MemoryPool& pool) VariableNode::VariableNode(MemoryPool& pool)
: TypedNode<ValueExprNode, ExprNode::TYPE_VARIABLE>(pool), : TypedNode<ValueExprNode, ExprNode::TYPE_VARIABLE>(pool),
dsqlName(pool), dsqlName(pool)
dsqlVar(NULL),
varDecl(NULL),
varInfo(NULL),
varId(0)
{ {
} }
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(); const USHORT n = csb->csb_blr_reader.getWord();
vec<DeclareVariableNode*>* vector = csb->csb_variables;
if (!vector || n >= vector->count())
PAR_error(csb, Arg::Gds(isc_badvarnum));
VariableNode* node = FB_NEW_POOL(pool) VariableNode(pool); VariableNode* node = FB_NEW_POOL(pool) VariableNode(pool);
node->varId = n; 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; return node;
} }
@ -13495,6 +13532,7 @@ string VariableNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, varId); NODE_PRINT(printer, varId);
NODE_PRINT(printer, varDecl); NODE_PRINT(printer, varDecl);
NODE_PRINT(printer, varInfo); NODE_PRINT(printer, varInfo);
NODE_PRINT(printer, outerDecl);
return "VariableNode"; return "VariableNode";
} }
@ -13505,6 +13543,35 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
node->dsqlName = dsqlName; node->dsqlName = dsqlName;
node->dsqlVar = dsqlVar ? dsqlVar.getObject() : dsqlScratch->resolveVariable(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) if (!node->dsqlVar)
PASS1_field_unknown(NULL, dsqlName.c_str(), this); PASS1_field_unknown(NULL, dsqlName.c_str(), this);
@ -13518,8 +13585,10 @@ void VariableNode::setParameterName(dsql_par* parameter) const
void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch) void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
bool execBlock = (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) && auto varScratch = outerDecl ? dsqlScratch->mainScratch : dsqlScratch;
!(dsqlScratch->flags &
const bool execBlock = (varScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) &&
!(varScratch->flags &
(DsqlCompilerScratch::FLAG_PROCEDURE | (DsqlCompilerScratch::FLAG_PROCEDURE |
DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_TRIGGER |
DsqlCompilerScratch::FLAG_FUNCTION)); DsqlCompilerScratch::FLAG_FUNCTION));
@ -13527,7 +13596,16 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch)
if (dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock) if (dsqlVar->type == dsql_var::TYPE_INPUT && !execBlock)
{ {
dsqlScratch->appendUChar(blr_parameter2); 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);
dsqlScratch->appendUShort(dsqlVar->msgItem + 1); 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. // If this is an EXECUTE BLOCK input parameter, use the internal variable.
dsqlScratch->appendUChar(blr_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) if (!o)
return false; 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->field->fld_name != o->dsqlVar->field->fld_name ||
dsqlVar->number != o->dsqlVar->number || dsqlVar->number != o->dsqlVar->number ||
dsqlVar->msgItem != o->dsqlVar->msgItem || dsqlVar->msgItem != o->dsqlVar->msgItem ||
@ -13562,6 +13649,19 @@ bool VariableNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* o
return true; 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) void VariableNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* /*csb*/, dsc* desc)
{ {
*desc = varDecl->varDesc; *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()); VariableNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) VariableNode(*tdbb->getDefaultPool());
node->varId = copier.csb->csb_remap_variable + varId; node->varId = copier.csb->csb_remap_variable + varId;
node->outerDecl = outerDecl;
node->varDecl = varDecl; node->varDecl = varDecl;
node->varInfo = varInfo; node->varInfo = varInfo;
@ -13591,12 +13692,14 @@ ValueExprNode* VariableNode::pass1(thread_db* tdbb, CompilerScratch* csb)
ValueExprNode* VariableNode::pass2(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); ValueExprNode::pass2(tdbb, csb);
if (nodFlags & FLAG_VALUE) if (varDecl->usedInSubRoutines)
impureOffset = csb->allocImpure<impure_value_ex>(); impureOffset = csb->allocImpure<impure_value>();
else else
impureOffset = csb->allocImpure<dsc>(); impureOffset = csb->allocImpure<dsc>();
@ -13605,31 +13708,68 @@ ValueExprNode* VariableNode::pass2(thread_db* tdbb, CompilerScratch* csb)
dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const dsc* VariableNode::execute(thread_db* tdbb, jrd_req* request) const
{ {
impure_value* const impure = request->getImpure<impure_value>(impureOffset); const auto varRequest = getVarRequest(request);
impure_value* impure2 = request->getImpure<impure_value>(varDecl->impureOffset); const auto varImpure = varRequest->getImpure<impure_value>(varDecl->impureOffset);
request->req_flags &= ~req_null; request->req_flags &= ~req_null;
if (impure2->vlu_desc.dsc_flags & DSC_null) dsc* desc;
request->req_flags |= req_null;
impure->vlu_desc = impure2->vlu_desc; if (varDecl->usedInSubRoutines)
if (impure->vlu_desc.dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
if (!(impure2->vlu_flags & VLU_checked))
{ {
if (varInfo) const auto impure = request->getImpure<impure_value>(impureOffset);
if (varImpure->vlu_desc.dsc_flags & DSC_null)
request->req_flags |= req_null;
else
{ {
EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, varId), varInfo, EVL_make_value(tdbb, &varImpure->vlu_desc, impure);
&impure->vlu_desc, (impure->vlu_desc.dsc_flags & DSC_null));
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<jrd_req*, thread_db> 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<dsc>(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;
} }

View File

@ -1579,10 +1579,7 @@ public:
ValueExprNode::getChildren(holder, dsql); ValueExprNode::getChildren(holder, dsql);
if (!dsql) if (!dsql)
{
holder.add(argFlag); holder.add(argFlag);
holder.add(argIndicator);
}
} }
virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual Firebird::string internalPrint(NodePrinter& printer) const;
@ -1598,6 +1595,8 @@ public:
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc); virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const; 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 void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
@ -1608,10 +1607,10 @@ public:
dsql_par* dsqlParameter = nullptr; dsql_par* dsqlParameter = nullptr;
NestConst<MessageNode> message; NestConst<MessageNode> message;
NestConst<ValueExprNode> argFlag; NestConst<ValueExprNode> argFlag;
NestConst<ValueExprNode> argIndicator;
NestConst<ItemInfo> argInfo; NestConst<ItemInfo> argInfo;
USHORT dsqlParameterIndex = 0; USHORT dsqlParameterIndex = 0;
USHORT argNumber = 0; USHORT argNumber = 0;
bool outerDecl = false;
}; };
@ -2201,6 +2200,8 @@ public:
dsqlDesc = desc; dsqlDesc = desc;
} }
jrd_req* getVarRequest(jrd_req* request) const;
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); virtual ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb);
@ -2212,7 +2213,8 @@ public:
NestConst<dsql_var> dsqlVar; NestConst<dsql_var> dsqlVar;
NestConst<DeclareVariableNode> varDecl; NestConst<DeclareVariableNode> varDecl;
NestConst<ItemInfo> varInfo; NestConst<ItemInfo> varInfo;
USHORT varId; USHORT varId = 0;
bool outerDecl = false;
}; };

View File

@ -539,8 +539,7 @@ public:
static const USHORT FLAG_DOUBLE = 0x20; static const USHORT FLAG_DOUBLE = 0x20;
static const USHORT FLAG_DATE = 0x40; static const USHORT FLAG_DATE = 0x40;
static const USHORT FLAG_DECFLOAT = 0x80; static const USHORT FLAG_DECFLOAT = 0x80;
static const USHORT FLAG_VALUE = 0x100; // Full value area required in impure space. static const USHORT FLAG_INT128 = 0x100;
static const USHORT FLAG_INT128 = 0x200;
explicit ExprNode(Type aType, MemoryPool& pool) explicit ExprNode(Type aType, MemoryPool& pool)
: DmlNode(pool), : DmlNode(pool),
@ -1409,6 +1408,7 @@ public:
TYPE_MERGE_SEND, TYPE_MERGE_SEND,
TYPE_MESSAGE, TYPE_MESSAGE,
TYPE_MODIFY, TYPE_MODIFY,
TYPE_OUTER_MAP,
TYPE_POST_EVENT, TYPE_POST_EVENT,
TYPE_RECEIVE, TYPE_RECEIVE,
TYPE_RETURN, TYPE_RETURN,

View File

@ -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; 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; return this;
} }
@ -2111,12 +2108,7 @@ void DeclareSubProcNode::genParameters(DsqlCompilerScratch* dsqlScratch,
} }
} }
DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/) DeclareSubProcNode* DeclareSubProcNode::pass1(thread_db* tdbb, CompilerScratch* /*csb*/)
{
return this;
}
DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch* /*csb*/)
{ {
ContextPoolHolder context(tdbb, &subCsb->csb_pool); ContextPoolHolder context(tdbb, &subCsb->csb_pool);
PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0); PAR_blr(tdbb, NULL, blrStart, blrLength, NULL, &subCsb, NULL, false, 0);
@ -2124,6 +2116,11 @@ DeclareSubProcNode* DeclareSubProcNode::pass2(thread_db* tdbb, CompilerScratch*
return this; 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 const StmtNode* DeclareSubProcNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeState* /*exeState*/) const
{ {
// Nothing to execute. This is the declaration node. // Nothing to execute. This is the declaration node.
@ -2207,6 +2204,9 @@ DeclareVariableNode* DeclareVariableNode::pass1(thread_db* tdbb, CompilerScratch
fb_assert(!(*vector)[varId]); fb_assert(!(*vector)[varId]);
(*vector)[varId] = this; (*vector)[varId] = this;
if (!csb->mainCsb && csb->csb_variables_used_in_subroutines.exist(varId))
usedInSubRoutines = true;
return this; return this;
} }
@ -4490,6 +4490,8 @@ void ExecBlockNode::genBlr(DsqlCompilerScratch* dsqlScratch)
dsqlScratch->loopLevel = 0; dsqlScratch->loopLevel = 0;
StmtNode* stmtNode = body->dsqlPass(dsqlScratch); StmtNode* stmtNode = body->dsqlPass(dsqlScratch);
dsqlScratch->putOuterMaps();
GEN_hidden_variables(dsqlScratch); GEN_hidden_variables(dsqlScratch);
dsqlScratch->appendUChar(blr_stall); dsqlScratch->appendUChar(blr_stall);
@ -7133,6 +7135,104 @@ const StmtNode* ModifyNode::modify(thread_db* tdbb, jrd_req* request, WhichTrigg
//-------------------- //--------------------
static RegisterNode<OuterMapNode> 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<DeclareVariableNode*>::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<PostEventNode> regPostEventNode({blr_post, blr_post_arg}); static RegisterNode<PostEventNode> regPostEventNode({blr_post, blr_post_arg});
DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)

View File

@ -477,9 +477,7 @@ class DeclareVariableNode final : public TypedNode<StmtNode, StmtNode::TYPE_DECL
{ {
public: public:
explicit DeclareVariableNode(MemoryPool& pool) explicit DeclareVariableNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_DECLARE_VARIABLE>(pool), : TypedNode<StmtNode, StmtNode::TYPE_DECLARE_VARIABLE>(pool)
dsqlDef(NULL),
varId(0)
{ {
varDesc.clear(); varDesc.clear();
} }
@ -498,7 +496,8 @@ public:
public: public:
NestConst<ParameterClause> dsqlDef; NestConst<ParameterClause> dsqlDef;
dsc varDesc; dsc varDesc;
USHORT varId; USHORT varId = 0;
bool usedInSubRoutines = false;
}; };
@ -1093,9 +1092,7 @@ class MessageNode : public TypedNode<StmtNode, StmtNode::TYPE_MESSAGE>
public: public:
explicit MessageNode(MemoryPool& pool) explicit MessageNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_MESSAGE>(pool), : TypedNode<StmtNode, StmtNode::TYPE_MESSAGE>(pool),
format(NULL), itemsUsedInSubroutines(pool)
impureFlags(0),
messageNumber(0)
{ {
} }
@ -1116,9 +1113,10 @@ public:
virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const; virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const;
public: public:
Firebird::SortedArray<USHORT> itemsUsedInSubroutines;
NestConst<Format> format; NestConst<Format> format;
ULONG impureFlags; ULONG impureFlags = 0;
USHORT messageNumber; USHORT messageNumber = 0;
}; };
@ -1171,6 +1169,40 @@ public:
}; };
class OuterMapNode final : public TypedNode<StmtNode, StmtNode::TYPE_OUTER_MAP>
{
public:
explicit OuterMapNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_OUTER_MAP>(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<StmtNode, StmtNode::TYPE_POST_EVENT> class PostEventNode final : public TypedNode<StmtNode, StmtNode::TYPE_POST_EVENT>
{ {
public: public:

View File

@ -77,14 +77,9 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch)
* Emit BLR for hidden variables. * Emit BLR for hidden variables.
* *
**************************************/ **************************************/
if (dsqlScratch->hiddenVariables.isEmpty())
return;
for (Array<dsql_var*>::const_iterator i = dsqlScratch->hiddenVariables.begin(); for (const auto var : dsqlScratch->hiddenVariables)
i != dsqlScratch->hiddenVariables.end();
++i)
{ {
const dsql_var* var = *i;
dsqlScratch->appendUChar(blr_dcl_variable); dsqlScratch->appendUChar(blr_dcl_variable);
dsqlScratch->appendUShort(var->number); dsqlScratch->appendUShort(var->number);
GEN_descriptor(dsqlScratch, &var->desc, true); GEN_descriptor(dsqlScratch, &var->desc, true);
@ -247,6 +242,7 @@ void GEN_request(DsqlCompilerScratch* scratch, DmlNode* node)
if (!block) if (!block)
scratch->appendUChar(blr_begin); scratch->appendUChar(blr_begin);
scratch->putOuterMaps();
GEN_hidden_variables(scratch); GEN_hidden_variables(scratch);
switch (statement->getType()) switch (statement->getType())

View File

@ -193,8 +193,8 @@
#define blr_agg_min (unsigned char)85 #define blr_agg_min (unsigned char)85
#define blr_agg_total (unsigned char)86 #define blr_agg_total (unsigned char)86
#define blr_agg_average (unsigned char)87 #define blr_agg_average (unsigned char)87
#define blr_parameter3 (unsigned char)88 /* same as Rdb definition */
/* unsupported /* unsupported
#define blr_parameter3 (unsigned char)88
#define blr_run_max (unsigned char)89 #define blr_run_max (unsigned char)89
#define blr_run_min (unsigned char)90 #define blr_run_min (unsigned char)90
#define blr_run_total (unsigned char)91 #define blr_run_total (unsigned char)91
@ -221,7 +221,7 @@
// unused codes: 111..117 // 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_rs_stream (unsigned char)119
#define blr_exec_proc (unsigned char)120 #define blr_exec_proc (unsigned char)120
@ -452,4 +452,8 @@
#define blr_local_table_truncate (unsigned char) 219 #define blr_local_table_truncate (unsigned char) 219
#define blr_local_table_id (unsigned char) 220 #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 #endif // FIREBIRD_IMPL_BLR_H

View File

@ -136,9 +136,6 @@ SortNode* SortNode::pass1(thread_db* tdbb, CompilerScratch* csb)
SortNode* SortNode::pass2(thread_db* tdbb, CompilerScratch* csb) SortNode* SortNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{ {
for (NestConst<ValueExprNode>* i = expressions.begin(); i != expressions.end(); ++i)
(*i)->nodFlags |= ExprNode::FLAG_VALUE;
for (NestConst<ValueExprNode>* i = expressions.begin(); i != expressions.end(); ++i) for (NestConst<ValueExprNode>* i = expressions.begin(); i != expressions.end(); ++i)
ExprNode::doPass2(tdbb, csb, i->getAddress()); ExprNode::doPass2(tdbb, csb, i->getAddress());

View File

@ -252,5 +252,6 @@ static const struct
{"dcl_local_table", dcl_local_table}, {"dcl_local_table", dcl_local_table},
{"local_table_truncate", one_word}, {"local_table_truncate", one_word},
{"local_table_id", local_table}, {"local_table_id", local_table},
{"outer_map", outer_map},
{0, 0} {0, 0}
}; };

View File

@ -136,25 +136,18 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node)
DEV_BLKCHK(node, type_nod); DEV_BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest(); jrd_req* request = tdbb->getRequest();
impure_value* impure = request->getImpure<impure_value>(node->impureOffset);
// The only nodes that can be assigned to are: argument, field and variable. // The only nodes that can be assigned to are: argument, field and variable.
int arg_number; if (auto paramNode = nodeAs<ParameterNode>(node))
const dsc* desc;
const MessageNode* message;
Record* record;
const ParameterNode* paramNode;
const VariableNode* varNode;
const FieldNode* fieldNode;
if ((paramNode = nodeAs<ParameterNode>(node)))
{ {
message = paramNode->message; auto message = paramNode->message;
arg_number = paramNode->argNumber; auto arg_number = paramNode->argNumber;
desc = &message->format->fmt_desc[arg_number]; auto desc = &message->format->fmt_desc[arg_number];
impure->vlu_desc.dsc_address = request->getImpure<UCHAR>( auto impure = request->getImpure<impure_value>(node->impureOffset);
impure->vlu_desc.dsc_address = paramNode->getParamRequest(request)->getImpure<UCHAR>(
message->impureOffset + (IPTR) desc->dsc_address); message->impureOffset + (IPTR) desc->dsc_address);
impure->vlu_desc.dsc_dtype = desc->dsc_dtype; impure->vlu_desc.dsc_dtype = desc->dsc_dtype;
impure->vlu_desc.dsc_length = desc->dsc_length; 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<NullNode>(node)) else if (nodeIs<NullNode>(node))
return NULL; return NULL;
else if ((varNode = nodeAs<VariableNode>(node))) else if (auto varNode = nodeAs<VariableNode>(node))
{ {
// Calculate descriptor auto impure = varNode->getVarRequest(request)->getImpure<impure_value>(varNode->varDecl->impureOffset);
impure = request->getImpure<impure_value>(varNode->varDecl->impureOffset);
return &impure->vlu_desc; return &impure->vlu_desc;
} }
else if ((fieldNode = nodeAs<FieldNode>(node))) else if (auto fieldNode = nodeAs<FieldNode>(node))
{ {
record = request->req_rpb[fieldNode->fieldStream].rpb_record; auto record = request->req_rpb[fieldNode->fieldStream].rpb_record;
auto impure = request->getImpure<impure_value>(node->impureOffset);
if (!EVL_field(0, record, fieldNode->fieldId, &impure->vlu_desc)) 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; request->req_flags = flags;
} }
Firebird::string s;
if (err) if (err)
{ {
ISC_STATUS status = isc_not_valid_for_var; ISC_STATUS status = isc_not_valid_for_var;
string s;
const char* arg; const char* arg;
if (item.type == Item::TYPE_CAST) if (item.type == Item::TYPE_CAST)

View File

@ -261,6 +261,14 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo
SET_TDBB(tdbb); SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest(); jrd_req* request = tdbb->getRequest();
const auto toVar = nodeAs<VariableNode>(to);
if (toVar && toVar->outerDecl)
request = toVar->getVarRequest(request);
AutoSetRestore2<jrd_req*, thread_db> autoSetRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, request);
// Get descriptors of receiving and sending fields/parameters, variables, etc. // Get descriptors of receiving and sending fields/parameters, variables, etc.
dsc* missing = NULL; dsc* missing = NULL;
@ -284,31 +292,39 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo
null = -1; null = -1;
USHORT* impure_flags = NULL; USHORT* impure_flags = NULL;
const ParameterNode* toParam; const auto toParam = nodeAs<ParameterNode>(to);
const VariableNode* toVar;
if ((toParam = nodeAs<ParameterNode>(to))) if (toParam)
{ {
const MessageNode* message = toParam->message; const MessageNode* message = toParam->message;
const auto paramRequest = toParam->getParamRequest(request);
if (toParam->argInfo) if (toParam->argInfo)
{ {
AutoSetRestore2<jrd_req*, thread_db> autoSetRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, paramRequest);
EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber), EVL_validate(tdbb, Item(Item::TYPE_PARAMETER, message->messageNumber, toParam->argNumber),
toParam->argInfo, from_desc, null == -1); toParam->argInfo, from_desc, null == -1);
} }
impure_flags = request->getImpure<USHORT>( impure_flags = paramRequest->getImpure<USHORT>(
message->impureFlags + (sizeof(USHORT) * toParam->argNumber)); message->impureFlags + (sizeof(USHORT) * toParam->argNumber));
} }
else if ((toVar = nodeAs<VariableNode>(to))) else if (toVar)
{ {
const auto varRequest = toVar->getVarRequest(request);
if (toVar->varInfo) if (toVar->varInfo)
{ {
AutoSetRestore2<jrd_req*, thread_db> autoSetRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, varRequest);
EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId), EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId),
toVar->varInfo, from_desc, null == -1); toVar->varInfo, from_desc, null == -1);
} }
impure_flags = &request->getImpure<impure_value>( impure_flags = &varRequest->getImpure<impure_value>(
toVar->varDecl->impureOffset)->vlu_flags; 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 (!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 // Validate range for datetime values
if (DTYPE_IS_DATE(from_desc->dsc_dtype)) 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. // Handle the null flag as appropriate for fields and message arguments.
const FieldNode* toField = nodeAs<FieldNode>(to); const FieldNode* toField = nodeAs<FieldNode>(to);
if (toField) 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_sub_type = 0;
temp.dsc_address = (UCHAR*) &null; temp.dsc_address = (UCHAR*) &null;
MOV_move(tdbb, &temp, to_desc); MOV_move(tdbb, &temp, to_desc);
if (null && toParam->argIndicator)
{
to_desc = EVL_assign_to(tdbb, toParam->argIndicator);
MOV_move(tdbb, &temp, to_desc);
}
} }
} }

View File

@ -36,6 +36,7 @@
#include "../jrd/Relation.h" #include "../jrd/Relation.h"
#include "../common/classes/array.h" #include "../common/classes/array.h"
#include "../jrd/MetaName.h" #include "../jrd/MetaName.h"
#include "../common/classes/fb_pair.h"
#include "../common/classes/NestConst.h" #include "../common/classes/NestConst.h"
#include "iberror.h" #include "iberror.h"
@ -453,12 +454,15 @@ public:
csb_current_nodes(p), csb_current_nodes(p),
csb_current_for_nodes(p), csb_current_for_nodes(p),
csb_computing_fields(p), csb_computing_fields(p),
csb_variables_used_in_subroutines(p),
csb_pool(p), csb_pool(p),
csb_map_field_info(p), csb_map_field_info(p),
csb_map_item_info(p), csb_map_item_info(p),
csb_message_pad(p), csb_message_pad(p),
subFunctions(p), subFunctions(p),
subProcedures(p), subProcedures(p),
outerMessagesMap(p),
outerVarsMap(p),
csb_currentForNode(NULL), csb_currentForNode(NULL),
csb_currentDMLNode(NULL), csb_currentDMLNode(NULL),
csb_currentAssignTarget(NULL), csb_currentAssignTarget(NULL),
@ -517,6 +521,7 @@ public:
// candidates within whose scope we are // candidates within whose scope we are
Firebird::Array<ForNode*> csb_current_for_nodes; Firebird::Array<ForNode*> csb_current_for_nodes;
Firebird::SortedArray<jrd_fld*> csb_computing_fields; // Computed fields being compiled Firebird::SortedArray<jrd_fld*> csb_computing_fields; // Computed fields being compiled
Firebird::SortedArray<USHORT> csb_variables_used_in_subroutines;
StreamType csb_n_stream; // Next available stream StreamType csb_n_stream; // Next available stream
USHORT csb_msg_number; // Highest used message number USHORT csb_msg_number; // Highest used message number
ULONG csb_impure; // Next offset into impure area ULONG csb_impure; // Next offset into impure area
@ -541,8 +546,10 @@ public:
bool csb_returning_expr; bool csb_returning_expr;
bool csb_implicit_cursor; bool csb_implicit_cursor;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubFuncNode*> > subFunctions; Firebird::LeftPooledMap<MetaName, DeclareSubFuncNode*> subFunctions;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubProcNode*> > subProcedures; Firebird::LeftPooledMap<MetaName, DeclareSubProcNode*> subProcedures;
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <inner, outer>
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <inner, outer>
ForNode* csb_currentForNode; ForNode* csb_currentForNode;
StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode StmtNode* csb_currentDMLNode; // could be StoreNode or ModifyNode

View File

@ -1141,6 +1141,7 @@ void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedu
MemoryPool& pool = *tdbb->getDefaultPool(); MemoryPool& pool = *tdbb->getDefaultPool();
// We have a few parameters. Get on with creating the message block // 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; USHORT n = ++csb->csb_msg_number;
if (n < 2) if (n < 2)
csb->csb_msg_number = n = 2; csb->csb_msg_number = n = 2;

View File

@ -318,6 +318,7 @@ const int op_subfunc_decl = 28;
const int op_window_win = 29; const int op_window_win = 29;
const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_erase = 30; // special due to optional blr_marks after blr_erase
const int op_dcl_local_table = 31; const int op_dcl_local_table = 31;
const int op_outer_map = 32;
static const UCHAR static const UCHAR
// generic print formats // generic print formats
@ -406,7 +407,8 @@ static const UCHAR
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0}, store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0},
marks[] = { op_byte, op_literal, op_line, op_verb, 0}, marks[] = { op_byte, op_literal, op_line, op_verb, 0},
erase[] = { op_erase, 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" #include "../jrd/blp.h"
@ -3885,6 +3887,46 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
break; 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: default:
fb_assert(false); fb_assert(false);
break; break;