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:
parent
e443f02dd3
commit
3c22c23874
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
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,6 +13613,14 @@ void VariableNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
// If this is an EXECUTE BLOCK input parameter, use the internal variable.
|
||||
dsqlScratch->appendUChar(blr_variable);
|
||||
|
||||
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 (varDecl->usedInSubRoutines)
|
||||
{
|
||||
const auto impure = request->getImpure<impure_value>(impureOffset);
|
||||
|
||||
if (varImpure->vlu_desc.dsc_flags & DSC_null)
|
||||
request->req_flags |= req_null;
|
||||
else
|
||||
{
|
||||
EVL_make_value(tdbb, &varImpure->vlu_desc, impure);
|
||||
|
||||
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
||||
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
||||
}
|
||||
|
||||
if (!(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,
|
||||
&impure->vlu_desc, (impure->vlu_desc.dsc_flags & DSC_null));
|
||||
desc, (desc->dsc_flags & DSC_null));
|
||||
}
|
||||
|
||||
impure2->vlu_flags |= VLU_checked;
|
||||
varImpure->vlu_flags |= VLU_checked;
|
||||
}
|
||||
}
|
||||
|
||||
return (request->req_flags & req_null) ? NULL : &impure->vlu_desc;
|
||||
return (request->req_flags & req_null) ? nullptr : desc;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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}
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user