8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 14:43: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:
<declaration item> ::=
DECLARE [VARIABLE] <variable name> <data type> [ := <value> ];
DECLARE [VARIABLE] <variable name> <data type> [ = <value> ];
|
DECLARE [VARIABLE] CURSOR <cursor name> FOR (<query>);
|
@ -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.

View File

@ -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,

View File

@ -345,30 +345,24 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT
if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE))
{
// Check not implemented sub-functions.
GenericMap<Left<MetaName, DeclareSubFuncNode*> >::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<Left<MetaName, DeclareSubProcNode*> >::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)

View File

@ -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<dsql_var*> outputVariables;
ReturningClause* returningClause;
const Firebird::string* const* currCteAlias;
DsqlCompilerScratch* mainScratch;
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <outer, inner>
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <outer, inner>
private:
Firebird::HalfStaticArray<SelectExprNode*, 4> ctes; // common table expressions
Firebird::HalfStaticArray<const Firebird::string*, 4> cteAliases; // CTE aliases in recursive members
bool psql;
DsqlCompilerScratch* mainScratch;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubFuncNode*> > subFunctions;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubProcNode*> > subProcedures;
Firebird::LeftPooledMap<MetaName, DeclareSubFuncNode*> subFunctions;
Firebird::LeftPooledMap<MetaName, DeclareSubProcNode*> subProcedures;
};
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)
: 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)
{
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<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)
@ -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<impure_value_ex>();
if (message->itemsUsedInSubroutines.exist(argNumber))
impureOffset = csb->allocImpure<impure_value>();
else
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
{
impure_value* const impure = request->getImpure<impure_value>(impureOffset);
request->req_flags &= ~req_null;
dsc* retDesc;
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;
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<UCHAR>(
retDesc->dsc_address = paramRequest->getImpure<UCHAR>(
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<USHORT>(
if (retDesc->dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, retDesc);
}
auto impureFlags = paramRequest->getImpure<USHORT>(
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<bid*>(desc->dsc_address);
const bid* const blobId = reinterpret_cast<bid*>(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<VariableNode> regVariableNode({blr_variable});
VariableNode::VariableNode(MemoryPool& pool)
: TypedNode<ValueExprNode, ExprNode::TYPE_VARIABLE>(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<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);
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<impure_value_ex>();
if (varDecl->usedInSubRoutines)
impureOffset = csb->allocImpure<impure_value>();
else
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
{
impure_value* const impure = request->getImpure<impure_value>(impureOffset);
impure_value* impure2 = request->getImpure<impure_value>(varDecl->impureOffset);
const auto varRequest = getVarRequest(request);
const auto varImpure = varRequest->getImpure<impure_value>(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<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,
&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<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);
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<MessageNode> message;
NestConst<ValueExprNode> argFlag;
NestConst<ValueExprNode> argIndicator;
NestConst<ItemInfo> 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<dsql_var> dsqlVar;
NestConst<DeclareVariableNode> varDecl;
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_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,

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;
}
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<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});
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:
explicit DeclareVariableNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_DECLARE_VARIABLE>(pool),
dsqlDef(NULL),
varId(0)
: TypedNode<StmtNode, StmtNode::TYPE_DECLARE_VARIABLE>(pool)
{
varDesc.clear();
}
@ -498,7 +496,8 @@ public:
public:
NestConst<ParameterClause> dsqlDef;
dsc varDesc;
USHORT varId;
USHORT varId = 0;
bool usedInSubRoutines = false;
};
@ -1093,9 +1092,7 @@ class MessageNode : public TypedNode<StmtNode, StmtNode::TYPE_MESSAGE>
public:
explicit MessageNode(MemoryPool& pool)
: TypedNode<StmtNode, StmtNode::TYPE_MESSAGE>(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<USHORT> itemsUsedInSubroutines;
NestConst<Format> format;
ULONG impureFlags;
USHORT messageNumber;
ULONG impureFlags = 0;
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>
{
public:

View File

@ -77,14 +77,9 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch)
* Emit BLR for hidden variables.
*
**************************************/
if (dsqlScratch->hiddenVariables.isEmpty())
return;
for (Array<dsql_var*>::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())

View File

@ -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

View File

@ -136,9 +136,6 @@ SortNode* SortNode::pass1(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)
ExprNode::doPass2(tdbb, csb, i->getAddress());

View File

@ -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}
};

View File

@ -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<impure_value>(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<ParameterNode>(node)))
if (auto paramNode = nodeAs<ParameterNode>(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<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);
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<NullNode>(node))
return NULL;
else if ((varNode = nodeAs<VariableNode>(node)))
else if (auto varNode = nodeAs<VariableNode>(node))
{
// Calculate descriptor
impure = request->getImpure<impure_value>(varNode->varDecl->impureOffset);
auto impure = varNode->getVarRequest(request)->getImpure<impure_value>(varNode->varDecl->impureOffset);
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))
{
@ -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)

View File

@ -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<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.
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<ParameterNode>(to);
if ((toParam = nodeAs<ParameterNode>(to)))
if (toParam)
{
const MessageNode* message = toParam->message;
const auto paramRequest = toParam->getParamRequest(request);
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),
toParam->argInfo, from_desc, null == -1);
}
impure_flags = request->getImpure<USHORT>(
impure_flags = paramRequest->getImpure<USHORT>(
message->impureFlags + (sizeof(USHORT) * toParam->argNumber));
}
else if ((toVar = nodeAs<VariableNode>(to)))
else if (toVar)
{
const auto varRequest = toVar->getVarRequest(request);
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),
toVar->varInfo, from_desc, null == -1);
}
impure_flags = &request->getImpure<impure_value>(
impure_flags = &varRequest->getImpure<impure_value>(
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<FieldNode>(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);
}
}
}

View File

@ -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<ForNode*> csb_current_for_nodes;
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
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<Firebird::Left<MetaName, DeclareSubFuncNode*> > subFunctions;
Firebird::GenericMap<Firebird::Left<MetaName, DeclareSubProcNode*> > subProcedures;
Firebird::LeftPooledMap<MetaName, DeclareSubFuncNode*> subFunctions;
Firebird::LeftPooledMap<MetaName, DeclareSubProcNode*> subProcedures;
Firebird::NonPooledMap<USHORT, USHORT> outerMessagesMap; // <inner, outer>
Firebird::NonPooledMap<USHORT, USHORT> outerVarsMap; // <inner, outer>
ForNode* csb_currentForNode;
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();
// 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;

View File

@ -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;