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

Named arguments for function call, EXECUTE PROCEDURE and procedure record source.

This commit is contained in:
Adriano dos Santos Fernandes 2023-09-19 07:14:02 -03:00
parent 57a629f013
commit 1b2b3ab638
24 changed files with 1798 additions and 530 deletions

View File

@ -0,0 +1,52 @@
# Named arguments for function and procedure calling (FB 6.0)
Named arguments allows you to specify function and procedure arguments by their names, rather than only by their positions.
It is especially useful when the routine have a lot of parameters and you want to specify them in arbitrary order or not specify some of them who have default values.
As the positional syntax, all arguments without default values are required to be present in the call.
It's currently not possible to mix positional and named arguments in the same call.
## Syntax
```
<function call> ::=
[<package name> .] <function_name>( [<arguments>] )
<procedure selection> ::=
[<package name> .] <procedure_name> [( <arguments> )]
<execute procedure> ::=
EXECUTE PROCEDURE [<package name> .] <procedure name>
[{ (<arguments>) | <arguments> }]
[RETURNING_VALUES ...]
<arguments> ::=
<named arguments>
<positional arguments>
<named arguments> ::=
<named argument> [ {, <named argument>}... ]
<named argument> ::=
<argument name> => <value>
```
## Examples
```
select function_name(parameter2 => 'Two', parameter1 => 1)
from rdb$database
```
```
execute procedure insert_customer(
last_name => 'SCHUMACHER',
first_name => 'MICHAEL')
```
```
select *
from get_customers(city_id => 10, last_name => 'SCHUMACHER')
```

View File

@ -46,6 +46,7 @@ PARSER_TOKEN('<', "<", false)
PARSER_TOKEN(TOK_LEQ, "<=", false) PARSER_TOKEN(TOK_LEQ, "<=", false)
PARSER_TOKEN(TOK_NEQ, "<>", false) // Alias of != PARSER_TOKEN(TOK_NEQ, "<>", false) // Alias of !=
PARSER_TOKEN('=', "=", false) PARSER_TOKEN('=', "=", false)
PARSER_TOKEN(TOK_NAMED_ARG_ASSIGN, "=>", false)
PARSER_TOKEN('>', ">", false) PARSER_TOKEN('>', ">", false)
PARSER_TOKEN(TOK_GEQ, ">=", false) PARSER_TOKEN(TOK_GEQ, ">=", false)
PARSER_TOKEN(TOK_BIND_PARAM, ":=", false) PARSER_TOKEN(TOK_BIND_PARAM, ":=", false)

View File

@ -12875,101 +12875,274 @@ dsc* TrimNode::execute(thread_db* tdbb, Request* request) const
//-------------------- //--------------------
static RegisterNode<UdfCallNode> regUdfCallNode({blr_function, blr_function2, blr_subfunc}); static RegisterNode<UdfCallNode> regUdfCallNode({blr_function, blr_function2, blr_subfunc, blr_invoke_function});
UdfCallNode::UdfCallNode(MemoryPool& pool, const QualifiedName& aName, ValueListNode* aArgs) UdfCallNode::UdfCallNode(MemoryPool& pool, const QualifiedName& aName,
ValueListNode* aArgs, ObjectsArray<MetaName>* aDsqlArgNames)
: TypedNode<ValueExprNode, ExprNode::TYPE_UDF_CALL>(pool), : TypedNode<ValueExprNode, ExprNode::TYPE_UDF_CALL>(pool),
name(pool, aName), name(pool, aName),
args(aArgs), args(aArgs),
function(NULL), dsqlArgNames(aDsqlArgNames)
dsqlFunction(NULL),
isSubRoutine(false)
{ {
} }
DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
const UCHAR blrOp) const UCHAR blrOp)
{ {
const UCHAR* savePos = csb->csb_blr_reader.getPos(); const auto predateCheck = [&](bool condition, const char* preVerb, const char* postVerb)
{
if (!condition)
{
string str;
str.printf("%s should predate %s", preVerb, postVerb);
PAR_error(csb, Arg::Gds(isc_random) << str);
}
};
auto& blrReader = csb->csb_blr_reader;
const UCHAR* startPos = csb->csb_blr_reader.getPos();
const UCHAR* argNamesPos = nullptr;
ObjectsArray<MetaName>* argNames = nullptr;
USHORT argCount = 0;
QualifiedName name; QualifiedName name;
if (blrOp == blr_function2) const auto node = FB_NEW_POOL(pool) UdfCallNode(pool);
csb->csb_blr_reader.getMetaName(name.package);
csb->csb_blr_reader.getMetaName(name.identifier); if (blrOp == blr_invoke_function)
const USHORT count = name.package.length() + name.identifier.length();
UdfCallNode* node = FB_NEW_POOL(pool) UdfCallNode(pool, name);
if (blrOp == blr_function &&
(name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT"))
{ {
csb->csb_blr_reader.setPos(savePos); UCHAR subCode;
return SysFuncCallNode::parse(tdbb, pool, csb, blr_sys_function);
}
if (blrOp == blr_subfunc) while ((subCode = blrReader.getByte()) != blr_end)
{
DeclareSubFuncNode* declareNode;
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
{ {
if (curCsb->subFunctions.get(name.identifier, declareNode)) switch (subCode)
node->function = declareNode->routine;
}
}
else if (!node->function)
node->function = Function::lookup(tdbb, name, false);
Function* function = node->function;
if (function)
{
if (function->isImplemented() && !function->isDefined())
{
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
{ {
PAR_warning(Arg::Warning(isc_funnotdef) << Arg::Str(name.toString()) << case blr_invoke_function_type:
Arg::Warning(isc_modnotfound)); {
} UCHAR functionType = blrReader.getByte();
else
{ switch (functionType)
csb->csb_blr_reader.seekBackward(count); {
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) << case blr_invoke_function_type_packaged:
Arg::Gds(isc_modnotfound)); blrReader.getMetaName(name.package);
break;
case blr_invoke_function_type_standalone:
case blr_invoke_function_type_sub:
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_function_type");
break;
}
blrReader.getMetaName(name.identifier);
if (functionType == blr_invoke_function_type_sub)
{
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
{
if (DeclareSubFuncNode* declareNode; curCsb->subFunctions.get(name.identifier, declareNode))
node->function = declareNode->routine;
}
}
else if (!node->function)
node->function = Function::lookup(tdbb, name, false);
break;
}
case blr_invoke_function_arg_names:
{
predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_arg_names");
predateCheck(!node->args, "blr_invoke_function_arg_names", "blr_invoke_function_arg_names");
argNamesPos = blrReader.getPos();
USHORT argNamesCount = blrReader.getWord();
MetaName argName;
argNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
while (argNamesCount--)
{
blrReader.getMetaName(argName);
argNames->add(argName);
}
break;
}
case blr_invoke_function_args:
predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_args");
argCount = blrReader.getWord();
node->args = PAR_args(tdbb, csb, argCount, (argNames ? argCount : MAX(argCount, node->function->fun_inputs)));
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_function sub code");
} }
} }
} }
else else
{ {
csb->csb_blr_reader.seekBackward(count); if (blrOp == blr_function2)
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString())); blrReader.getMetaName(name.package);
blrReader.getMetaName(name.identifier);
if (blrOp == blr_function &&
(name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT"))
{
blrReader.setPos(startPos);
return SysFuncCallNode::parse(tdbb, pool, csb, blr_sys_function);
}
if (blrOp == blr_subfunc)
{
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
{
if (DeclareSubFuncNode* declareNode; curCsb->subFunctions.get(name.identifier, declareNode))
node->function = declareNode->routine;
}
}
else if (!node->function)
node->function = Function::lookup(tdbb, name, false);
argCount = blrReader.getByte();
node->args = PAR_args(tdbb, csb, argCount, node->function->fun_inputs);
} }
node->isSubRoutine = function->isSubRoutine(); if (!node->function)
const UCHAR argCount = csb->csb_blr_reader.getByte();
// Check to see if the argument count matches.
if (argCount < function->fun_inputs - function->getDefaultCount() || argCount > function->fun_inputs)
PAR_error(csb, Arg::Gds(isc_funmismat) << name.toString());
node->args = PAR_args(tdbb, csb, argCount, function->fun_inputs);
for (USHORT i = argCount; i < function->fun_inputs; ++i)
{ {
Parameter* const parameter = function->getInputFields()[i]; blrReader.setPos(startPos);
node->args->items[i] = CMP_clone_node(tdbb, csb, parameter->prm_default_value); PAR_error(csb, Arg::Gds(isc_funnotdef) << name.toString());
} }
if (node->function->isImplemented() && !node->function->isDefined())
{
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
{
PAR_warning(Arg::Warning(isc_funnotdef) << name.toString() <<
Arg::Warning(isc_modnotfound));
}
else
{
blrReader.setPos(startPos);
PAR_error(csb, Arg::Gds(isc_funnotdef) << name.toString() <<
Arg::Gds(isc_modnotfound));
}
}
node->name = name;
node->isSubRoutine = node->function->isSubRoutine();
Arg::StatusVector mismatchStatus;
mismatchStatus << Arg::Gds(isc_fun_param_mismatch) << name.toString();
const auto mismatchInitialLength = mismatchStatus.length();
if (!node->args)
node->args = FB_NEW_POOL(pool) ValueListNode(pool);
if (argNames && argNames->getCount() != node->args->items.getCount())
{
blrReader.setPos(argNamesPos);
PAR_error(csb,
Arg::Gds(isc_random) << "blr_invoke_function_arg_names count differs from blr_invoke_function_args");
}
if (argNames)
{
LeftPooledMap<MetaName, NestConst<ValueExprNode>> argsByName;
auto argIt = node->args->items.begin();
for (const auto& argName : *argNames)
{
if (argsByName.put(argName, *argIt++))
mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << argName;
}
node->args->items.resize(node->function->getInputFields().getCount());
argIt = node->args->items.begin();
for (auto& parameter : node->function->getInputFields())
{
if (const auto argValue = argsByName.get(parameter->prm_name))
{
*argIt = *argValue;
argsByName.remove(parameter->prm_name);
}
else
{
if (parameter->prm_default_value)
*argIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
else
mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name;
}
++argIt;
}
if (argsByName.hasData())
{
for (const auto& argPair : argsByName)
mismatchStatus << Arg::Gds(isc_param_not_exist) << argPair.first;
}
}
else
{
// Check to see if the argument count matches.
if (argCount > node->function->fun_inputs)
mismatchStatus << Arg::Gds(isc_wronumarg);
else if (argCount < node->function->fun_inputs - node->function->getDefaultCount())
{
unsigned pos = 0;
for (auto& parameter : node->function->getInputFields())
{
if (++pos <= argCount)
continue;
mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name;
if (pos >= node->function->fun_inputs - node->function->getDefaultCount())
break;
}
}
else
{
for (unsigned pos = argCount; pos < node->function->getInputFields().getCount(); ++pos)
{
auto parameter = node->function->getInputFields()[pos];
fb_assert(parameter->prm_default_value);
node->args->items[pos] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
}
}
}
if (mismatchStatus.length() > mismatchInitialLength)
status_exception::raise(mismatchStatus);
// CVC: I will track ufds only if a function is not being dropped. // CVC: I will track ufds only if a function is not being dropped.
if (!function->isSubRoutine() && csb->collectingDependencies()) if (!node->function->isSubRoutine() && csb->collectingDependencies())
{ {
CompilerScratch::Dependency dependency(obj_udf); { // scope
dependency.function = function; CompilerScratch::Dependency dependency(obj_udf);
csb->addDependency(dependency); dependency.function = node->function;
csb->addDependency(dependency);
}
if (argNames)
{
for (const auto& argName : *argNames)
{
CompilerScratch::Dependency dependency(obj_udf);
dependency.function = node->function;
dependency.subName = &argName;
csb->addDependency(dependency);
}
}
} }
return node; return node;
@ -12992,6 +13165,45 @@ void UdfCallNode::setParameterName(dsql_par* parameter) const
void UdfCallNode::genBlr(DsqlCompilerScratch* dsqlScratch) void UdfCallNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
if (dsqlArgNames || args->items.getCount() >= UCHAR_MAX)
{
dsqlScratch->appendUChar(blr_invoke_function);
dsqlScratch->appendUChar(blr_invoke_function_type);
if (dsqlFunction->udf_name.package.hasData())
{
dsqlScratch->appendUChar(blr_invoke_function_type_packaged);
dsqlScratch->appendMetaString(dsqlFunction->udf_name.package.c_str());
}
else
{
dsqlScratch->appendUChar((dsqlFunction->udf_flags & UDF_subfunc) ?
blr_invoke_function_type_sub : blr_invoke_function_type_standalone);
}
dsqlScratch->appendMetaString(dsqlFunction->udf_name.identifier.c_str());
if (dsqlArgNames && dsqlArgNames->hasData())
{
dsqlScratch->appendUChar(blr_invoke_function_arg_names);
dsqlScratch->appendUShort(dsqlArgNames->getCount());
for (auto& argName : *dsqlArgNames)
dsqlScratch->appendMetaString(argName.c_str());
}
dsqlScratch->appendUChar(blr_invoke_function_args);
dsqlScratch->appendUShort(args->items.getCount());
for (auto& arg : args->items)
GEN_expr(dsqlScratch, arg);
dsqlScratch->appendUChar(blr_end);
return;
}
if (dsqlFunction->udf_name.package.isEmpty()) if (dsqlFunction->udf_name.package.isEmpty())
dsqlScratch->appendUChar((dsqlFunction->udf_flags & UDF_subfunc) ? blr_subfunc : blr_function); dsqlScratch->appendUChar((dsqlFunction->udf_flags & UDF_subfunc) ? blr_subfunc : blr_function);
else else
@ -13355,8 +13567,11 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const
ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
{ {
UdfCallNode* node = FB_NEW_POOL(dsqlScratch->getPool()) UdfCallNode(dsqlScratch->getPool(), name, const auto node = FB_NEW_POOL(dsqlScratch->getPool()) UdfCallNode(dsqlScratch->getPool(), name,
doDsqlPass(dsqlScratch, args)); doDsqlPass(dsqlScratch, args),
dsqlArgNames ?
FB_NEW_POOL(dsqlScratch->getPool()) ObjectsArray<MetaName>(dsqlScratch->getPool(), *dsqlArgNames) :
nullptr);
if (name.package.isEmpty()) if (name.package.isEmpty())
{ {
@ -13374,28 +13589,56 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
Arg::Gds(isc_random) << Arg::Str(name.toString())); Arg::Gds(isc_random) << Arg::Str(name.toString()));
} }
const USHORT arg_count = node->dsqlFunction->udf_arguments.getCount(); if (node->dsqlArgNames)
const USHORT count = node->args->items.getCount();
if (count > arg_count || count < arg_count - node->dsqlFunction->udf_def_count)
ERRD_post(Arg::Gds(isc_fun_param_mismatch) << Arg::Str(name.toString()));
unsigned pos = 0;
for (auto& arg : node->args->items)
{ {
if (pos < node->dsqlFunction->udf_arguments.getCount()) fb_assert(node->dsqlArgNames->getCount() == node->args->items.getCount());
LeftPooledMap<MetaName, const dsc*> argsByName;
for (const auto& arg : node->dsqlFunction->udf_arguments)
argsByName.put(arg.name, &arg.desc);
bool mismatchError = false;
Arg::StatusVector mismatchStatus;
mismatchStatus << Arg::Gds(isc_fun_param_mismatch) << Arg::Str(name.toString());
auto argIt = node->args->items.begin();
for (const auto& argName : *node->dsqlArgNames)
{ {
PASS1_set_parameter_type(dsqlScratch, arg, if (const auto argDescPtr = argsByName.get(argName))
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos]; }, {
false); PASS1_set_parameter_type(dsqlScratch, *argIt,
} [&] (dsc* desc) { *desc = **argDescPtr; },
else false);
{ }
// We should complain here in the future! The parameter is else
// out of bounds or the function doesn't declare input params. {
mismatchError = true;
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
}
++argIt;
} }
++pos; if (mismatchError)
status_exception::raise(mismatchStatus);
}
else
{
unsigned pos = 0;
for (auto& arg : node->args->items)
{
if (pos < node->dsqlFunction->udf_arguments.getCount())
{
PASS1_set_parameter_type(dsqlScratch, arg,
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos].desc; },
false);
}
++pos;
}
} }
return node; return node;

View File

@ -2134,11 +2134,12 @@ private:
}; };
public: public:
explicit UdfCallNode(MemoryPool& pool, const QualifiedName& aName, UdfCallNode(MemoryPool& pool, const QualifiedName& aName = {},
ValueListNode* aArgs = NULL); ValueListNode* aArgs = nullptr, Firebird::ObjectsArray<MetaName>* aDsqlArgNames = nullptr);
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
public:
virtual void getChildren(NodeRefsHolder& holder, bool dsql) const virtual void getChildren(NodeRefsHolder& holder, bool dsql) const
{ {
ValueExprNode::getChildren(holder, dsql); ValueExprNode::getChildren(holder, dsql);
@ -2172,11 +2173,12 @@ public:
public: public:
QualifiedName name; QualifiedName name;
NestConst<ValueListNode> args; NestConst<ValueListNode> args;
NestConst<Firebird::ObjectsArray<MetaName>> dsqlArgNames;
NestConst<Function> function; NestConst<Function> function;
private: private:
dsql_udf* dsqlFunction; dsql_udf* dsqlFunction = nullptr;
bool isSubRoutine; bool isSubRoutine = false;
}; };

View File

@ -1270,6 +1270,12 @@ public:
items.push(arg1); items.push(arg1);
} }
ValueListNode(MemoryPool& pool)
: TypedNode<ListExprNode, ExprNode::TYPE_VALUE_LIST>(pool),
items(pool, INITIAL_CAPACITY)
{
}
virtual void getChildren(NodeRefsHolder& holder, bool dsql) const virtual void getChildren(NodeRefsHolder& holder, bool dsql) const
{ {
ListExprNode::getChildren(holder, dsql); ListExprNode::getChildren(holder, dsql);

View File

@ -1664,8 +1664,8 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
if (!implemetingForward) if (!implemetingForward)
{ {
// ASF: dsqlFunction->udf_arguments is only checked for its count for now. // ASF: dsqlFunction->udf_arguments types (desc) are not checked for now.
dsqlFunction->udf_arguments.add(dsc()); dsqlFunction->udf_arguments.add().name = param->name;
} }
if (param->defaultClause) if (param->defaultClause)
@ -2905,79 +2905,304 @@ const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, Request* request,
static RegisterNode<ExecProcedureNode> regExecProcedureNode( static RegisterNode<ExecProcedureNode> regExecProcedureNode(
{blr_exec_proc, blr_exec_proc2, blr_exec_pid, blr_exec_subproc}); {blr_exec_proc, blr_exec_proc2, blr_exec_pid, blr_exec_subproc, blr_invoke_procedure});
// Parse an execute procedure reference. // Parse an execute procedure reference.
DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
{ {
SET_TDBB(tdbb); const auto predateCheck = [&](bool condition, const char* preVerb, const char* postVerb)
{
if (!condition)
{
string str;
str.printf("%s should predate %s", preVerb, postVerb);
PAR_error(csb, Arg::Gds(isc_random) << str);
}
};
const auto blrStartPos = csb->csb_blr_reader.getPos(); auto& blrReader = csb->csb_blr_reader;
jrd_prc* procedure = NULL; const auto blrStartPos = blrReader.getPos();
const UCHAR* inArgNamesPos = nullptr;
ObjectsArray<MetaName>* inArgNames = nullptr;
USHORT inArgCount = 0;
const UCHAR* outArgNamesPos = nullptr;
ObjectsArray<MetaName>* outArgNames = nullptr;
USHORT outArgCount = 0;
QualifiedName name; QualifiedName name;
if (blrOp == blr_exec_pid) const auto node = FB_NEW_POOL(pool) ExecProcedureNode(pool);
{
const USHORT pid = csb->csb_blr_reader.getWord();
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
name.identifier.printf("id %d", pid);
}
else
{
if (blrOp == blr_exec_proc2)
csb->csb_blr_reader.getMetaName(name.package);
csb->csb_blr_reader.getMetaName(name.identifier); switch (blrOp)
{
if (blrOp == blr_exec_subproc) case blr_invoke_procedure:
{ {
DeclareSubProcNode* declareNode; UCHAR subCode;
for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb) while ((subCode = blrReader.getByte()) != blr_end)
{ {
if (curCsb->subProcedures.get(name.identifier, declareNode)) switch (subCode)
procedure = declareNode->routine; {
case blr_invsel_procedure_type:
{
UCHAR procedureType = blrReader.getByte();
switch (procedureType)
{
case blr_invsel_procedure_type_packaged:
blrReader.getMetaName(name.package);
break;
case blr_invsel_procedure_type_standalone:
case blr_invsel_procedure_type_sub:
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invsel_procedure_type");
break;
}
blrReader.getMetaName(name.identifier);
if (procedureType == blr_invsel_procedure_type_sub)
{
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
{
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
node->procedure = (*declareNode)->routine;
}
}
else if (!node->procedure)
node->procedure = MET_lookup_procedure(tdbb, name, false);
break;
}
case blr_invsel_procedure_in_arg_names:
{
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_arg_names");
predateCheck(!node->inputSources,
"blr_invsel_procedure_in_arg_names", "blr_invsel_procedure_in_args");
inArgNamesPos = blrReader.getPos();
USHORT inArgNamesCount = blrReader.getWord();
MetaName argName;
inArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
while (inArgNamesCount--)
{
blrReader.getMetaName(argName);
inArgNames->add(argName);
}
break;
}
case blr_invsel_procedure_in_args:
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_args");
inArgCount = blrReader.getWord();
node->inputSources = PAR_args(tdbb, csb, inArgCount,
(inArgNames ? inArgCount : MAX(inArgCount, node->procedure->getInputFields().getCount())));
break;
case blr_invsel_procedure_out_arg_names:
{
predateCheck(node->procedure,
"blr_invsel_procedure_type", "blr_invsel_procedure_out_arg_names");
predateCheck(!node->outputTargets,
"blr_invsel_procedure_out_arg_names", "blr_invsel_procedure_out_args");
outArgNamesPos = blrReader.getPos();
USHORT outArgNamesCount = blrReader.getWord();
MetaName argName;
outArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
while (outArgNamesCount--)
{
blrReader.getMetaName(argName);
outArgNames->add(argName);
}
break;
}
case blr_invsel_procedure_out_args:
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_out_args");
outArgCount = blrReader.getWord();
node->outputTargets = PAR_args(tdbb, csb, outArgCount, outArgCount);
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_procedure sub code");
}
} }
}
else
procedure = MET_lookup_procedure(tdbb, name, false);
}
if (!procedure) break;
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString())); }
else
{ case blr_exec_pid:
if (procedure->isImplemented() && !procedure->isDefined())
{ {
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) const USHORT pid = blrReader.getWord();
if (!(node->procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
name.identifier.printf("id %d", pid);
break;
}
default:
if (blrOp == blr_exec_proc2)
blrReader.getMetaName(name.package);
blrReader.getMetaName(name.identifier);
if (blrOp == blr_exec_subproc)
{ {
PAR_warning( for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << {
Arg::Warning(isc_modnotfound)); if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
node->procedure = (*declareNode)->routine;
}
} }
else else
node->procedure = MET_lookup_procedure(tdbb, name, false);
break;
}
if (!node->procedure)
{
blrReader.setPos(blrStartPos);
PAR_error(csb, Arg::Gds(isc_prcnotdef) << name.toString());
}
if (blrOp != blr_invoke_procedure)
{
inArgCount = blrReader.getWord();
node->inputSources = PAR_args(tdbb, csb, inArgCount, inArgCount);
outArgCount = blrReader.getWord();
node->outputTargets = PAR_args(tdbb, csb, outArgCount, outArgCount);
}
if (!node->inputSources)
node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool);
if (!node->outputTargets)
node->outputTargets = FB_NEW_POOL(pool) ValueListNode(pool);
if (inArgNames && inArgNames->getCount() != node->inputSources->items.getCount())
{
blrReader.setPos(inArgNamesPos);
PAR_error(csb,
Arg::Gds(isc_random) <<
"blr_invsel_procedure_in_arg_names count differs from blr_invsel_procedure_in_args");
}
if (outArgNames && outArgNames->getCount() != node->outputTargets->items.getCount())
{
blrReader.setPos(outArgNamesPos);
PAR_error(csb,
Arg::Gds(isc_random) <<
"blr_invsel_procedure_out_arg_names count differs from blr_invsel_procedure_out_args");
}
if (node->procedure->isImplemented() && !node->procedure->isDefined())
{
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
{
PAR_warning(
Arg::Warning(isc_prcnotdef) << name.toString() <<
Arg::Warning(isc_modnotfound));
}
else
{
csb->csb_blr_reader.setPos(blrStartPos);
PAR_error(csb,
Arg::Gds(isc_prcnotdef) << name.toString() <<
Arg::Gds(isc_modnotfound));
}
}
Arg::StatusVector mismatchStatus;
mismatchStatus << Arg::Gds(isc_prcmismat) << node->procedure->getName().toString();
const auto mismatchInitialLength = mismatchStatus.length();
node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount());
mismatchStatus << CMP_procedure_arguments(
tdbb,
csb,
node->procedure,
true,
inArgCount,
inArgNames,
node->inputSources,
node->inputTargets,
node->inputMessage);
mismatchStatus << CMP_procedure_arguments(
tdbb,
csb,
node->procedure,
false,
outArgCount,
outArgNames,
node->outputTargets,
node->outputSources,
node->outputMessage);
if (mismatchStatus.length() > mismatchInitialLength)
status_exception::raise(mismatchStatus);
if (csb->collectingDependencies() && !node->procedure->isSubRoutine())
{
{ // scope
CompilerScratch::Dependency dependency(obj_procedure);
dependency.procedure = node->procedure;
csb->addDependency(dependency);
}
if (inArgNames)
{
for (const auto& argName : *inArgNames)
{ {
csb->csb_blr_reader.setPos(blrStartPos); CompilerScratch::Dependency dependency(obj_procedure);
PAR_error(csb, dependency.procedure = node->procedure;
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << dependency.subName = &argName;
Arg::Gds(isc_modnotfound)); csb->addDependency(dependency);
}
}
if (outArgNames)
{
for (const auto& argName : *outArgNames)
{
CompilerScratch::Dependency dependency(obj_procedure);
dependency.procedure = node->procedure;
dependency.subName = &argName;
csb->addDependency(dependency);
} }
} }
} }
ExecProcedureNode* node = FB_NEW_POOL(pool) ExecProcedureNode(pool); if (node->inputSources && node->inputSources->items.isEmpty())
node->procedure = procedure;
PAR_procedure_parms(tdbb, csb, procedure, node->inputMessage.getAddress(),
node->inputSources.getAddress(), node->inputTargets.getAddress(), true);
PAR_procedure_parms(tdbb, csb, procedure, node->outputMessage.getAddress(),
node->outputSources.getAddress(), node->outputTargets.getAddress(), false);
if (csb->collectingDependencies() && !procedure->isSubRoutine())
{ {
CompilerScratch::Dependency dependency(obj_procedure); delete node->inputSources.getObject();
dependency.procedure = procedure; node->inputSources = nullptr;
csb->addDependency(dependency);
delete node->inputTargets.getObject();
node->inputTargets = nullptr;
}
if (node->outputSources && node->outputSources->items.isEmpty())
{
delete node->outputSources.getObject();
node->outputSources = nullptr;
delete node->outputTargets.getObject();
node->outputTargets = nullptr;
} }
return node; return node;
@ -3007,7 +3232,13 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (!dsqlScratch->isPsql()) if (!dsqlScratch->isPsql())
dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE); dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE);
ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName); const auto node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName,
doDsqlPass(dsqlScratch, inputSources),
nullptr,
dsqlInputArgNames ?
FB_NEW_POOL(dsqlScratch->getPool()) ObjectsArray<MetaName>(dsqlScratch->getPool(), *dsqlInputArgNames) :
nullptr);
node->dsqlProcedure = procedure; node->dsqlProcedure = procedure;
if (node->dsqlName.package.isEmpty() && procedure->prc_name.package.hasData()) if (node->dsqlName.package.isEmpty() && procedure->prc_name.package.hasData())
@ -3015,28 +3246,63 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
// Handle input parameters. // Handle input parameters.
const USHORT count = inputSources ? inputSources->items.getCount() : 0; if (node->dsqlInputArgNames)
if (count > procedure->prc_in_count || count < procedure->prc_in_count - procedure->prc_def_count)
ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(dsqlName.toString()));
node->inputSources = doDsqlPass(dsqlScratch, inputSources);
if (count)
{ {
// Initialize this stack variable, and make it look like a node. fb_assert(node->dsqlInputArgNames->getCount() == node->inputSources->items.getCount());
dsc desc_node;
NestConst<ValueExprNode>* ptr = node->inputSources->items.begin(); LeftPooledMap<MetaName, const dsql_fld*> argsByName;
const NestConst<ValueExprNode>* end = node->inputSources->items.end();
for (const dsql_fld* field = procedure->prc_inputs; ptr != end; ++ptr, field = field->fld_next) for (const auto* field = procedure->prc_inputs; field; field = field->fld_next)
argsByName.put(field->fld_name, field);
bool mismatchError = false;
Arg::StatusVector mismatchStatus;
mismatchStatus << Arg::Gds(isc_prcmismat) << Arg::Str(dsqlName.toString());
auto argIt = node->inputSources->items.begin();
for (const auto& argName : *node->dsqlInputArgNames)
{ {
DEV_BLKCHK(field, dsql_type_fld); if (const auto field = argsByName.get(argName))
DEV_BLKCHK(*ptr, dsql_type_nod); {
DsqlDescMaker::fromField(&desc_node, field); dsc descNode;
PASS1_set_parameter_type(dsqlScratch, *ptr, DsqlDescMaker::fromField(&descNode, *field);
[&] (dsc* desc) { *desc = desc_node; },
false); PASS1_set_parameter_type(dsqlScratch, *argIt,
[&] (dsc* desc) { *desc = descNode; },
false);
}
else
{
mismatchError = true;
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
}
++argIt;
}
if (mismatchError)
status_exception::raise(mismatchStatus);
}
else
{
if (inputSources && inputSources->items.hasData())
{
// Initialize this stack variable, and make it look like a node.
dsc descNode;
auto ptr = node->inputSources->items.begin();
const auto end = node->inputSources->items.end();
for (const dsql_fld* field = procedure->prc_inputs; field && ptr != end; ++ptr, field = field->fld_next)
{
DEV_BLKCHK(field, dsql_type_fld);
DEV_BLKCHK(*ptr, dsql_type_nod);
DsqlDescMaker::fromField(&descNode, field);
PASS1_set_parameter_type(dsqlScratch, *ptr,
[&] (dsc* desc) { *desc = descNode; },
false);
}
} }
} }
@ -3125,7 +3391,7 @@ string ExecProcedureNode::internalPrint(NodePrinter& printer) const
void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch) void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
const dsql_msg* message = NULL; const dsql_msg* message = nullptr;
if (dsqlScratch->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE) if (dsqlScratch->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE)
{ {
@ -3137,44 +3403,94 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
} }
} }
if (dsqlName.package.hasData()) if (dsqlInputArgNames)
{ {
dsqlScratch->appendUChar(blr_exec_proc2); dsqlScratch->appendUChar(blr_invoke_procedure);
dsqlScratch->appendMetaString(dsqlName.package.c_str());
dsqlScratch->appendUChar(blr_invsel_procedure_type);
if (dsqlName.package.hasData())
{
dsqlScratch->appendUChar(blr_invsel_procedure_type_packaged);
dsqlScratch->appendMetaString(dsqlName.package.c_str());
}
else
{
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ?
blr_invsel_procedure_type_sub : blr_invsel_procedure_type_standalone);
}
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
// Input parameters.
if (inputSources)
{
if (dsqlInputArgNames && dsqlInputArgNames->hasData())
{
dsqlScratch->appendUChar(blr_invsel_procedure_in_arg_names);
dsqlScratch->appendUShort(dsqlInputArgNames->getCount());
for (auto& argName : *dsqlInputArgNames)
dsqlScratch->appendMetaString(argName.c_str());
}
dsqlScratch->appendUChar(blr_invsel_procedure_in_args);
dsqlScratch->appendUShort(inputSources->items.getCount());
for (auto& arg : inputSources->items)
GEN_expr(dsqlScratch, arg);
}
// Output parameters.
if (outputSources)
{
dsqlScratch->appendUChar(blr_invsel_procedure_out_args);
dsqlScratch->appendUShort(outputSources->items.getCount());
for (auto& arg : outputSources->items)
GEN_expr(dsqlScratch, arg);
}
dsqlScratch->appendUChar(blr_end);
} }
else else
{ {
dsqlScratch->appendUChar( if (dsqlName.package.hasData())
(dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc); {
dsqlScratch->appendUChar(blr_exec_proc2);
dsqlScratch->appendMetaString(dsqlName.package.c_str());
}
else
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc);
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
// Input parameters.
if (inputSources)
{
dsqlScratch->appendUShort(inputSources->items.getCount());
auto ptr = inputSources->items.begin();
const auto end = inputSources->items.end();
while (ptr < end)
GEN_expr(dsqlScratch, *ptr++);
}
else
dsqlScratch->appendUShort(0);
// Output parameters.
if (outputSources)
{
dsqlScratch->appendUShort(outputSources->items.getCount());
auto ptr = outputSources->items.begin();
for (const auto end = outputSources->items.end(); ptr != end; ++ptr)
GEN_expr(dsqlScratch, *ptr);
}
else
dsqlScratch->appendUShort(0);
} }
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
// Input parameters.
if (inputSources)
{
dsqlScratch->appendUShort(inputSources->items.getCount());
NestConst<ValueExprNode>* ptr = inputSources->items.begin();
const NestConst<ValueExprNode>* end = inputSources->items.end();
while (ptr < end)
GEN_expr(dsqlScratch, *ptr++);
}
else
dsqlScratch->appendUShort(0);
// Output parameters.
if (outputSources)
{
dsqlScratch->appendUShort(outputSources->items.getCount());
NestConst<ValueExprNode>* ptr = outputSources->items.begin();
for (const NestConst<ValueExprNode>* end = outputSources->items.end(); ptr != end; ++ptr)
GEN_expr(dsqlScratch, *ptr);
}
else
dsqlScratch->appendUShort(0);
if (message) if (message)
dsqlScratch->appendUChar(blr_end); dsqlScratch->appendUChar(blr_end);
} }

View File

@ -589,23 +589,20 @@ class ExecProcedureNode final : public TypedNode<StmtNode, StmtNode::TYPE_EXEC_P
public: public:
explicit ExecProcedureNode(MemoryPool& pool, explicit ExecProcedureNode(MemoryPool& pool,
const QualifiedName& aDsqlName = QualifiedName(), const QualifiedName& aDsqlName = QualifiedName(),
ValueListNode* aInputs = NULL, ValueListNode* aOutputs = NULL) ValueListNode* aInputs = nullptr, ValueListNode* aOutputs = nullptr,
Firebird::ObjectsArray<MetaName>* aDsqlInputArgNames = nullptr)
: TypedNode<StmtNode, StmtNode::TYPE_EXEC_PROCEDURE>(pool), : TypedNode<StmtNode, StmtNode::TYPE_EXEC_PROCEDURE>(pool),
dsqlName(pool, aDsqlName), dsqlName(pool, aDsqlName),
dsqlProcedure(NULL),
inputSources(aInputs), inputSources(aInputs),
inputTargets(NULL),
inputMessage(NULL),
outputSources(aOutputs), outputSources(aOutputs),
outputTargets(NULL), dsqlInputArgNames(aDsqlInputArgNames)
outputMessage(NULL),
procedure(NULL)
{ {
} }
public: public:
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual ExecProcedureNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); virtual ExecProcedureNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
virtual void genBlr(DsqlCompilerScratch* dsqlScratch); virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
@ -619,7 +616,7 @@ private:
public: public:
QualifiedName dsqlName; QualifiedName dsqlName;
dsql_prc* dsqlProcedure; dsql_prc* dsqlProcedure = nullptr;
NestConst<ValueListNode> inputSources; NestConst<ValueListNode> inputSources;
NestConst<ValueListNode> inputTargets; NestConst<ValueListNode> inputTargets;
NestConst<MessageNode> inputMessage; NestConst<MessageNode> inputMessage;
@ -627,6 +624,7 @@ public:
NestConst<ValueListNode> outputTargets; NestConst<ValueListNode> outputTargets;
NestConst<MessageNode> outputMessage; NestConst<MessageNode> outputMessage;
NestConst<jrd_prc> procedure; NestConst<jrd_prc> procedure;
NestConst<Firebird::ObjectsArray<MetaName>> dsqlInputArgNames;
}; };

View File

@ -318,6 +318,19 @@ enum prc_flags_vals {
//! User defined function block //! User defined function block
class dsql_udf : public pool_alloc<dsql_type_udf> class dsql_udf : public pool_alloc<dsql_type_udf>
{ {
public:
class Argument
{
public:
Argument(MemoryPool& p)
: name(p)
{}
public:
MetaName name;
dsc desc;
};
public: public:
explicit dsql_udf(MemoryPool& p) explicit dsql_udf(MemoryPool& p)
: udf_name(p), : udf_name(p),
@ -332,7 +345,7 @@ public:
SSHORT udf_character_set_id = 0; SSHORT udf_character_set_id = 0;
USHORT udf_flags = 0; USHORT udf_flags = 0;
QualifiedName udf_name; QualifiedName udf_name;
Firebird::Array<dsc> udf_arguments; Firebird::ObjectsArray<Argument> udf_arguments;
bool udf_private = false; // Packaged private function bool udf_private = false; // Packaged private function
SSHORT udf_def_count = 0; // number of inputs with default values SSHORT udf_def_count = 0; // number of inputs with default values
}; };

View File

@ -770,7 +770,9 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat
defaults++; defaults++;
} }
userFunc->udf_arguments.add(d); auto& argument = userFunc->udf_arguments.add();
argument.name = X.RDB$ARGUMENT_NAME;
argument.desc = d;
} }
} }
END_FOR END_FOR
@ -842,7 +844,9 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat
defaults++; defaults++;
} }
userFunc->udf_arguments.add(d); auto& argument = userFunc->udf_arguments.add();
argument.name = X.RDB$ARGUMENT_NAME;
argument.desc = d;
} }
} }
} }

View File

@ -699,6 +699,7 @@ using namespace Firebird;
// tokens added for Firebird 6.0 // tokens added for Firebird 6.0
%token <metaNamePtr> ANY_VALUE %token <metaNamePtr> ANY_VALUE
%token <metaNamePtr> NAMED_ARG_ASSIGN
// precedence declarations for expression evaluation // precedence declarations for expression evaluation
@ -761,6 +762,8 @@ using namespace Firebird;
Jrd::DbFileClause* dbFileClause; Jrd::DbFileClause* dbFileClause;
Firebird::Array<NestConst<Jrd::DbFileClause> >* dbFilesClause; Firebird::Array<NestConst<Jrd::DbFileClause> >* dbFilesClause;
Jrd::ExternalClause* externalClause; Jrd::ExternalClause* externalClause;
Firebird::NonPooledPair<Jrd::MetaName*, Jrd::ValueExprNode*>* namedArgument;
Firebird::NonPooledPair<Firebird::ObjectsArray<Jrd::MetaName>*, Jrd::ValueListNode*>* namedArguments;
Firebird::Array<NestConst<Jrd::ParameterClause> >* parametersClause; Firebird::Array<NestConst<Jrd::ParameterClause> >* parametersClause;
Jrd::WindowClause* windowClause; Jrd::WindowClause* windowClause;
Jrd::WindowClause::FrameExtent* windowClauseFrameExtent; Jrd::WindowClause::FrameExtent* windowClauseFrameExtent;
@ -3753,16 +3756,28 @@ fetch_scroll($cursorStmtNode)
%type <stmtNode> exec_procedure %type <stmtNode> exec_procedure
exec_procedure exec_procedure
: EXECUTE PROCEDURE symbol_procedure_name proc_inputs proc_outputs_opt : EXECUTE PROCEDURE symbol_procedure_name proc_inputs proc_outputs_opt
{ $$ = newNode<ExecProcedureNode>(QualifiedName(*$3), $4, $5); } {
$$ = newNode<ExecProcedureNode>(
QualifiedName(*$3),
($4 ? $4->second : nullptr),
$5,
($4 ? $4->first : nullptr));
}
| EXECUTE PROCEDURE symbol_package_name '.' symbol_procedure_name proc_inputs proc_outputs_opt | EXECUTE PROCEDURE symbol_package_name '.' symbol_procedure_name proc_inputs proc_outputs_opt
{ $$ = newNode<ExecProcedureNode>(QualifiedName(*$5, *$3), $6, $7); } {
$$ = newNode<ExecProcedureNode>(
QualifiedName(*$5, *$3),
($6 ? $6->second : nullptr),
$7,
($6 ? $6->first : nullptr));
}
; ;
%type <valueListNode> proc_inputs %type <namedArguments> proc_inputs
proc_inputs proc_inputs
: /* nothing */ { $$ = NULL; } : /* nothing */ { $$ = nullptr; }
| value_list { $$ = $1; } | argument_list { $$ = $1; }
| '(' value_list ')' { $$ = $2; } | '(' argument_list ')' { $$ = $2; }
; ;
%type <valueListNode> proc_outputs_opt %type <valueListNode> proc_outputs_opt
@ -6248,38 +6263,42 @@ named_columns_join
table_proc table_proc
: symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name : symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name
{ {
ProcedureSourceNode* node = newNode<ProcedureSourceNode>(QualifiedName(*$1)); const auto node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->sourceList = $2; node->inputSources = $2 ? $2->second : nullptr;
node->dsqlInputArgNames = $2 ? $2->first : nullptr;
node->alias = $4->c_str(); node->alias = $4->c_str();
$$ = node; $$ = node;
} }
| symbol_procedure_name table_proc_inputs | symbol_procedure_name table_proc_inputs
{ {
ProcedureSourceNode* node = newNode<ProcedureSourceNode>(QualifiedName(*$1)); const auto node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->sourceList = $2; node->inputSources = $2 ? $2->second : nullptr;
node->dsqlInputArgNames = $2 ? $2->first : nullptr;
$$ = node; $$ = node;
} }
| symbol_package_name '.' symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name | symbol_package_name '.' symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name
{ {
ProcedureSourceNode* node = newNode<ProcedureSourceNode>( const auto node = newNode<ProcedureSourceNode>(
QualifiedName(*$3, *$1)); QualifiedName(*$3, *$1));
node->sourceList = $4; node->inputSources = $4 ? $4->second : nullptr;
node->dsqlInputArgNames = $4 ? $4->first : nullptr;
node->alias = $6->c_str(); node->alias = $6->c_str();
$$ = node; $$ = node;
} }
| symbol_package_name '.' symbol_procedure_name table_proc_inputs | symbol_package_name '.' symbol_procedure_name table_proc_inputs
{ {
ProcedureSourceNode* node = newNode<ProcedureSourceNode>( const auto node = newNode<ProcedureSourceNode>(
QualifiedName(*$3, *$1)); QualifiedName(*$3, *$1));
node->sourceList = $4; node->inputSources = $4 ? $4->second : nullptr;
node->dsqlInputArgNames = $4 ? $4->first : nullptr;
$$ = node; $$ = node;
} }
; ;
%type <valueListNode> table_proc_inputs %type <namedArguments> table_proc_inputs
table_proc_inputs table_proc_inputs
: /* nothing */ { $$ = NULL; } : /* nothing */ { $$ = nullptr; }
| '(' value_list ')' { $$ = $2; } | '(' argument_list ')' { $$ = $2; }
; ;
%type <relSourceNode> table_name %type <relSourceNode> table_name
@ -8580,14 +8599,55 @@ trim_specification
%type <valueExprNode> udf %type <valueExprNode> udf
udf udf
: symbol_UDF_call_name '(' value_list ')' : symbol_UDF_call_name '(' argument_list_opt ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), $3); } { $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), $3->second, $3->first); }
| symbol_UDF_call_name '(' ')' | symbol_package_name '.' symbol_UDF_name '(' argument_list_opt ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), newNode<ValueListNode>(0)); } { $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), $5->second, $5->first); }
| symbol_package_name '.' symbol_UDF_name '(' value_list ')' ;
{ $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), $5); }
| symbol_package_name '.' symbol_UDF_name '(' ')' %type <namedArguments> argument_list_opt
{ $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), newNode<ValueListNode>(0)); } argument_list_opt
: // nothing
{
$$ = newNode<NonPooledPair<ObjectsArray<MetaName>*, ValueListNode*>>();
$$->second = newNode<ValueListNode>();
}
| argument_list
;
%type <namedArguments> argument_list
argument_list
: named_argument_list
{ $$ = $1; }
| value_list
{
$$ = newNode<NonPooledPair<ObjectsArray<MetaName>*, ValueListNode*>>();
$$->second = $1;
}
;
%type <namedArguments> named_argument_list
named_argument_list
: named_argument
{
$$ = newNode<NonPooledPair<ObjectsArray<MetaName>*, ValueListNode*>>();
$$->first = newNode<ObjectsArray<MetaName>>();
$$->first->add(*$1->first);
$$->second = newNode<ValueListNode>();
$$->second->add($1->second);
}
| named_argument_list ',' named_argument
{
$$ = $1;
$$->first->add(*$3->first);
$$->second->add($3->second);
}
;
%type <namedArgument> named_argument
named_argument
: symbol_column_name NAMED_ARG_ASSIGN value
{ $$ = newNode<NonPooledPair<MetaName*, ValueExprNode*>>($1, $3); }
; ;
%type <valueExprNode> cast_specification %type <valueExprNode> cast_specification

View File

@ -371,19 +371,16 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{ {
// No processing needed here for derived tables. // No processing needed here for derived tables.
} }
else if (procNode && (procNode->dsqlName.package.hasData() || procNode->sourceList)) else if (procNode && (procNode->dsqlName.package.hasData() || procNode->inputSources))
{ {
if (procNode->dsqlName.package.isEmpty()) if (procNode->dsqlName.package.isEmpty())
{ {
DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); const auto subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
procedure = subProcedure ? subProcedure->dsqlProcedure : NULL; procedure = subProcedure ? subProcedure->dsqlProcedure : NULL;
} }
if (!procedure) if (!procedure)
{ procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch, procNode->dsqlName);
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch,
procNode->dsqlName);
}
if (!procedure) if (!procedure)
{ {
@ -401,7 +398,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{ {
if (procNode && procNode->dsqlName.package.isEmpty()) if (procNode && procNode->dsqlName.package.isEmpty())
{ {
DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); const auto subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
procedure = subProcedure ? subProcedure->dsqlProcedure : NULL; procedure = subProcedure ? subProcedure->dsqlProcedure : NULL;
} }
@ -409,10 +406,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name); relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
if (!relation && !procedure && procNode) if (!relation && !procedure && procNode)
{ procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch, procNode->dsqlName);
procedure = METD_get_procedure(dsqlScratch->getTransaction(),
dsqlScratch, procNode->dsqlName);
}
if (!relation && !procedure) if (!relation && !procedure)
{ {
@ -433,7 +427,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
} }
// Set up context block. // Set up context block.
dsql_ctx* context = FB_NEW_POOL(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool()); const auto context = FB_NEW_POOL(*tdbb->getDefaultPool()) dsql_ctx(*tdbb->getDefaultPool());
context->ctx_relation = relation; context->ctx_relation = relation;
context->ctx_procedure = procedure; context->ctx_procedure = procedure;
@ -535,9 +529,9 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{ {
USHORT count = 0; USHORT count = 0;
if (procNode->sourceList) if (procNode->inputSources)
{ {
context->ctx_proc_inputs = Node::doDsqlPass(dsqlScratch, procNode->sourceList, false); context->ctx_proc_inputs = Node::doDsqlPass(dsqlScratch, procNode->inputSources, false);
count = context->ctx_proc_inputs->items.getCount(); count = context->ctx_proc_inputs->items.getCount();
} }
@ -551,8 +545,8 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{ {
// Initialize this stack variable, and make it look like a node // Initialize this stack variable, and make it look like a node
dsc desc_node; dsc desc_node;
ValueListNode* inputList = context->ctx_proc_inputs; auto inputList = context->ctx_proc_inputs;
NestConst<ValueExprNode>* input = inputList->items.begin(); auto input = inputList->items.begin();
for (dsql_fld* field = procedure->prc_inputs; for (dsql_fld* field = procedure->prc_inputs;
input != inputList->items.end(); input != inputList->items.end();
@ -1677,21 +1671,22 @@ RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceN
DEV_BLKCHK(dsqlScratch, dsql_type_req); DEV_BLKCHK(dsqlScratch, dsql_type_req);
dsql_ctx* context = PASS1_make_context(dsqlScratch, input); const auto context = PASS1_make_context(dsqlScratch, input);
RecordSourceNode* node = NULL;
if (context->ctx_relation) if (context->ctx_relation)
{ {
RelationSourceNode* relNode = FB_NEW_POOL(*tdbb->getDefaultPool()) RelationSourceNode( const auto relNode = FB_NEW_POOL(*tdbb->getDefaultPool()) RelationSourceNode(
*tdbb->getDefaultPool(), context->ctx_relation->rel_name); *tdbb->getDefaultPool(), context->ctx_relation->rel_name);
relNode->dsqlContext = context; relNode->dsqlContext = context;
return relNode; return relNode;
} }
else if (context->ctx_procedure) else if (context->ctx_procedure)
{ {
ProcedureSourceNode* procNode = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode( const auto procNode = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode(
*tdbb->getDefaultPool(), context->ctx_procedure->prc_name); *tdbb->getDefaultPool(), context->ctx_procedure->prc_name);
procNode->dsqlContext = context; procNode->dsqlContext = context;
procNode->inputSources = context->ctx_proc_inputs;
procNode->dsqlInputArgNames = nodeAs<ProcedureSourceNode>(input)->dsqlInputArgNames;
return procNode; return procNode;
} }
//// TODO: LocalTableSourceNode //// TODO: LocalTableSourceNode

View File

@ -467,4 +467,29 @@
#define blr_skip_locked (unsigned char) 223 #define blr_skip_locked (unsigned char) 223
// FB 6.0 specific BLR
#define blr_invoke_function (unsigned char) 224
#define blr_invoke_function_type (unsigned char) 1
#define blr_invoke_function_type_standalone (unsigned char) 1
#define blr_invoke_function_type_packaged (unsigned char) 2
#define blr_invoke_function_type_sub (unsigned char) 3
#define blr_invoke_function_arg_names (unsigned char) 2
#define blr_invoke_function_args (unsigned char) 3
#define blr_invoke_procedure (unsigned char) 225
#define blr_select_procedure (unsigned char) 226
// subcodes of blr_invoke_procedure and blr_select_procedure
#define blr_invsel_procedure_type (unsigned char) 1
#define blr_invsel_procedure_type_standalone (unsigned char) 1
#define blr_invsel_procedure_type_packaged (unsigned char) 2
#define blr_invsel_procedure_type_sub (unsigned char) 3
#define blr_invsel_procedure_in_arg_names (unsigned char) 2
#define blr_invsel_procedure_in_args (unsigned char) 3
#define blr_invsel_procedure_out_arg_names (unsigned char) 4
#define blr_invsel_procedure_out_args (unsigned char) 5
#define blr_invsel_procedure_context (unsigned char) 6
#define blr_invsel_procedure_alias (unsigned char) 7
#endif // FIREBIRD_IMPL_BLR_H #endif // FIREBIRD_IMPL_BLR_H

View File

@ -190,7 +190,7 @@ FB_IMPL_MSG(JRD, 188, range_not_found, -834, "42", "000", "refresh range number
FB_IMPL_MSG(JRD, 189, charset_not_found, -204, "2C", "000", "CHARACTER SET @1 is not defined") FB_IMPL_MSG(JRD, 189, charset_not_found, -204, "2C", "000", "CHARACTER SET @1 is not defined")
FB_IMPL_MSG(JRD, 190, lock_timeout, -901, "40", "001", "lock time-out on wait transaction") FB_IMPL_MSG(JRD, 190, lock_timeout, -901, "40", "001", "lock time-out on wait transaction")
FB_IMPL_MSG(JRD, 191, prcnotdef, -204, "42", "000", "procedure @1 is not defined") FB_IMPL_MSG(JRD, 191, prcnotdef, -204, "42", "000", "procedure @1 is not defined")
FB_IMPL_MSG(JRD, 192, prcmismat, -170, "07", "001", "Input parameter mismatch for procedure @1") FB_IMPL_MSG(JRD, 192, prcmismat, -170, "07", "001", "Parameter mismatch for procedure @1")
FB_IMPL_MSG(JRD, 193, wal_bugcheck, -246, "XX", "000", "Database @1: WAL subsystem bug for pid @2\n@3") FB_IMPL_MSG(JRD, 193, wal_bugcheck, -246, "XX", "000", "Database @1: WAL subsystem bug for pid @2\n@3")
FB_IMPL_MSG(JRD, 194, wal_cant_expand, -247, "HY", "000", "Could not expand the WAL segment for database @1") FB_IMPL_MSG(JRD, 194, wal_cant_expand, -247, "HY", "000", "Could not expand the WAL segment for database @1")
FB_IMPL_MSG(JRD, 195, codnotdef, -204, "HY", "000", "status code @1 unknown") FB_IMPL_MSG(JRD, 195, codnotdef, -204, "HY", "000", "status code @1 unknown")
@ -779,7 +779,7 @@ FB_IMPL_MSG(JRD, 777, crdb_load, -901, "08", "004", "@1 failed when working with
FB_IMPL_MSG(JRD, 778, crdb_nodb, -901, "0A", "000", "CREATE DATABASE grants check is not possible when database @1 is not present") FB_IMPL_MSG(JRD, 778, crdb_nodb, -901, "0A", "000", "CREATE DATABASE grants check is not possible when database @1 is not present")
FB_IMPL_MSG(JRD, 779, crdb_notable, -901, "0A", "000", "CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @1") FB_IMPL_MSG(JRD, 779, crdb_notable, -901, "0A", "000", "CREATE DATABASE grants check is not possible when table RDB$DB_CREATORS is not present in database @1")
FB_IMPL_MSG(JRD, 780, interface_version_too_old, -804, "HY", "000", "Interface @3 version too old: expected @1, found @2") FB_IMPL_MSG(JRD, 780, interface_version_too_old, -804, "HY", "000", "Interface @3 version too old: expected @1, found @2")
FB_IMPL_MSG(JRD, 781, fun_param_mismatch, -170, "07", "001", "Input parameter mismatch for function @1") FB_IMPL_MSG(JRD, 781, fun_param_mismatch, -170, "07", "001", "Parameter mismatch for function @1")
FB_IMPL_MSG(JRD, 782, savepoint_backout_err, -901, "HY", "000", "Error during savepoint backout - transaction invalidated") FB_IMPL_MSG(JRD, 782, savepoint_backout_err, -901, "HY", "000", "Error during savepoint backout - transaction invalidated")
FB_IMPL_MSG(JRD, 783, domain_primary_key_notnull, -291, "42", "000", "Domain used in the PRIMARY KEY constraint of table @1 must be NOT NULL") FB_IMPL_MSG(JRD, 783, domain_primary_key_notnull, -291, "42", "000", "Domain used in the PRIMARY KEY constraint of table @1 must be NOT NULL")
FB_IMPL_MSG(JRD, 784, invalid_attachment_charset, -204, "2C", "000", "CHARACTER SET @1 cannot be used as a attachment character set") FB_IMPL_MSG(JRD, 784, invalid_attachment_charset, -204, "2C", "000", "CHARACTER SET @1 cannot be used as a attachment character set")
@ -969,3 +969,6 @@ FB_IMPL_MSG(JRD, 966, bad_par_workers, -924, "HY", "000", "Wrong parallel worker
FB_IMPL_MSG(JRD, 967, idx_expr_not_found, -902, "42", "000", "Definition of index expression is not found for index @1") FB_IMPL_MSG(JRD, 967, idx_expr_not_found, -902, "42", "000", "Definition of index expression is not found for index @1")
FB_IMPL_MSG(JRD, 968, idx_cond_not_found, -902, "42", "000", "Definition of index condition is not found for index @1") FB_IMPL_MSG(JRD, 968, idx_cond_not_found, -902, "42", "000", "Definition of index condition is not found for index @1")
FB_IMPL_MSG(JRD, 969, uninitialized_var, -625, "42", "000", "Variable @1 is not initialized") FB_IMPL_MSG(JRD, 969, uninitialized_var, -625, "42", "000", "Variable @1 is not initialized")
FB_IMPL_MSG(JRD, 970, param_not_exist, -170, "07", "001", "Parameter @1 does not exist")
FB_IMPL_MSG(JRD, 971, param_no_default_not_specified, -170, "07", "001", "Parameter @1 has no default value and was not specified")
FB_IMPL_MSG(JRD, 972, param_multiple_assignments, -170, "07", "001", "Parameter @1 has multiple assignments")

View File

@ -5701,6 +5701,9 @@ const
isc_idx_expr_not_found = 335545287; isc_idx_expr_not_found = 335545287;
isc_idx_cond_not_found = 335545288; isc_idx_cond_not_found = 335545288;
isc_uninitialized_var = 335545289; isc_uninitialized_var = 335545289;
isc_param_not_exist = 335545290;
isc_param_no_default_not_specified = 335545291;
isc_param_multiple_assignments = 335545292;
isc_gfix_db_name = 335740929; isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930; isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932; isc_gfix_incmp_sw = 335740932;

View File

@ -896,27 +896,144 @@ RecordSource* RelationSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool
ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch* csb, ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch* csb,
const SSHORT blrOp, bool parseContext) const SSHORT blrOp, bool parseContext)
{ {
SET_TDBB(tdbb); const auto predateCheck = [&](bool condition, const char* preVerb, const char* postVerb)
{
if (!condition)
{
string str;
str.printf("%s should predate %s", preVerb, postVerb);
PAR_error(csb, Arg::Gds(isc_random) << str);
}
};
const auto blrStartPos = csb->csb_blr_reader.getPos(); auto& pool = *tdbb->getDefaultPool();
jrd_prc* procedure = nullptr; auto& blrReader = csb->csb_blr_reader;
AutoPtr<string> aliasString; const auto blrStartPos = blrReader.getPos();
const UCHAR* inArgNamesPos = nullptr;
ObjectsArray<MetaName>* inArgNames = nullptr;
USHORT inArgCount = 0;
QualifiedName name; QualifiedName name;
const auto node = FB_NEW_POOL(pool) ProcedureSourceNode(pool);
switch (blrOp) switch (blrOp)
{ {
case blr_select_procedure:
{
CompilerScratch::csb_repeat* csbTail = nullptr;
UCHAR subCode;
while ((subCode = blrReader.getByte()) != blr_end)
{
switch (subCode)
{
case blr_invsel_procedure_type:
{
UCHAR procedureType = blrReader.getByte();
switch (procedureType)
{
case blr_invsel_procedure_type_packaged:
blrReader.getMetaName(name.package);
break;
case blr_invsel_procedure_type_standalone:
case blr_invsel_procedure_type_sub:
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invsel_procedure_type");
break;
}
blrReader.getMetaName(name.identifier);
if (procedureType == blr_invsel_procedure_type_sub)
{
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
{
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
node->procedure = (*declareNode)->routine;
}
}
else if (!node->procedure)
node->procedure = MET_lookup_procedure(tdbb, name, false);
break;
}
case blr_invsel_procedure_in_arg_names:
{
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_arg_names");
predateCheck(!node->inputSources, "blr_invsel_procedure_in_arg_names", "blr_invsel_procedure_in_args");
inArgNamesPos = blrReader.getPos();
USHORT inArgNamesCount = blrReader.getWord();
MetaName argName;
inArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
while (inArgNamesCount--)
{
blrReader.getMetaName(argName);
inArgNames->add(argName);
}
break;
}
case blr_invsel_procedure_in_args:
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_args");
inArgCount = blrReader.getWord();
node->inputSources = PAR_args(tdbb, csb, inArgCount,
(inArgNames ? inArgCount : MAX(inArgCount, node->procedure->getInputFields().getCount())));
break;
case blr_invsel_procedure_context:
if (!parseContext)
{
PAR_error(csb,
Arg::Gds(isc_random) <<
"blr_invsel_procedure_context not expected inside plan clauses");
}
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_context");
node->stream = PAR_context2(csb, &node->context);
csbTail = &csb->csb_rpt[node->stream];
csbTail->csb_procedure = node->procedure;
if (node->alias.hasData())
csbTail->csb_alias = &node->alias;
if (csb->collectingDependencies())
PAR_dependency(tdbb, csb, node->stream, (SSHORT) -1, "");
break;
case blr_invsel_procedure_alias:
blrReader.getString(node->alias);
if (csbTail)
csbTail->csb_alias = &node->alias;
break;
default:
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_select_procedure sub code");
}
}
break;
}
case blr_pid: case blr_pid:
case blr_pid2: case blr_pid2:
{ {
const SSHORT pid = csb->csb_blr_reader.getWord(); const SSHORT pid = blrReader.getWord();
if (blrOp == blr_pid2) if (blrOp == blr_pid2)
{ blrReader.getString(node->alias);
aliasString = FB_NEW_POOL(csb->csb_pool) string(csb->csb_pool);
csb->csb_blr_reader.getString(*aliasString);
}
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0))) if (!(node->procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
name.identifier.printf("id %d", pid); name.identifier.printf("id %d", pid);
break; break;
@ -928,31 +1045,23 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
case blr_procedure4: case blr_procedure4:
case blr_subproc: case blr_subproc:
if (blrOp == blr_procedure3 || blrOp == blr_procedure4) if (blrOp == blr_procedure3 || blrOp == blr_procedure4)
csb->csb_blr_reader.getMetaName(name.package); blrReader.getMetaName(name.package);
csb->csb_blr_reader.getMetaName(name.identifier); blrReader.getMetaName(name.identifier);
if (blrOp == blr_procedure2 || blrOp == blr_procedure4 || blrOp == blr_subproc) if (blrOp == blr_procedure2 || blrOp == blr_procedure4 || blrOp == blr_subproc)
{ blrReader.getString(node->alias);
aliasString = FB_NEW_POOL(csb->csb_pool) string(csb->csb_pool);
csb->csb_blr_reader.getString(*aliasString);
if (blrOp == blr_subproc && aliasString->isEmpty())
aliasString.reset();
}
if (blrOp == blr_subproc) if (blrOp == blr_subproc)
{ {
DeclareSubProcNode* declareNode; for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb)
{ {
if (curCsb->subProcedures.get(name.identifier, declareNode)) if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
procedure = declareNode->routine; node->procedure = (*declareNode)->routine;
} }
} }
else else
procedure = MET_lookup_procedure(tdbb, name, false); node->procedure = MET_lookup_procedure(tdbb, name, false);
break; break;
@ -960,60 +1069,112 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
fb_assert(false); fb_assert(false);
} }
if (!procedure) if (!node->procedure)
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
else
{ {
if (procedure->isImplemented() && !procedure->isDefined()) blrReader.setPos(blrStartPos);
PAR_error(csb, Arg::Gds(isc_prcnotdef) << name.toString());
}
if (node->procedure->prc_type == prc_executable)
{
if (tdbb->getAttachment()->isGbak())
PAR_warning(Arg::Warning(isc_illegal_prc_type) << node->procedure->getName().toString());
else
PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << node->procedure->getName().toString());
}
node->isSubRoutine = node->procedure->isSubRoutine();
node->procedureId = node->isSubRoutine ? 0 : node->procedure->getId();
if (node->procedure->isImplemented() && !node->procedure->isDefined())
{
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
{ {
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator)) PAR_warning(
{ Arg::Warning(isc_prcnotdef) << name.toString() <<
PAR_warning( Arg::Warning(isc_modnotfound));
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) << }
Arg::Warning(isc_modnotfound)); else
{
blrReader.setPos(blrStartPos);
PAR_error(csb,
Arg::Gds(isc_prcnotdef) << name.toString() <<
Arg::Gds(isc_modnotfound));
}
}
if (parseContext)
{
if (blrOp != blr_select_procedure)
{
node->stream = PAR_context(csb, &node->context);
csb->csb_rpt[node->stream].csb_procedure = node->procedure;
csb->csb_rpt[node->stream].csb_alias = &node->alias;
inArgCount = blrReader.getWord();
node->inputSources = PAR_args(tdbb, csb, inArgCount, inArgCount);
}
if (!node->inputSources)
node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool);
if (inArgNames && inArgNames->getCount() != node->inputSources->items.getCount())
{
blrReader.setPos(inArgNamesPos);
PAR_error(csb,
Arg::Gds(isc_random) <<
"blr_invsel_procedure_in_arg_names count differs from blr_invsel_procedure_in_args");
}
Arg::StatusVector mismatchStatus;
mismatchStatus << Arg::Gds(isc_prcmismat) << node->procedure->getName().toString();
const auto mismatchInitialLength = mismatchStatus.length();
node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount());
mismatchStatus << CMP_procedure_arguments(
tdbb,
csb,
node->procedure,
true,
inArgCount,
inArgNames,
node->inputSources,
node->inputTargets,
node->inputMessage);
if (mismatchStatus.length() > mismatchInitialLength)
status_exception::raise(mismatchStatus);
if (csb->collectingDependencies() && !node->procedure->isSubRoutine())
{
{ // scope
CompilerScratch::Dependency dependency(obj_procedure);
dependency.procedure = node->procedure;
csb->addDependency(dependency);
} }
else
if (inArgNames)
{ {
csb->csb_blr_reader.setPos(blrStartPos); for (const auto& argName : *inArgNames)
PAR_error(csb, {
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) << CompilerScratch::Dependency dependency(obj_procedure);
Arg::Gds(isc_modnotfound)); dependency.procedure = node->procedure;
dependency.subName = &argName;
csb->addDependency(dependency);
}
} }
} }
} }
if (procedure->prc_type == prc_executable) if (node->inputSources && node->inputSources->items.isEmpty())
{ {
const string name = procedure->getName().toString(); delete node->inputSources.getObject();
node->inputSources = nullptr;
if (tdbb->getAttachment()->isGbak()) delete node->inputTargets.getObject();
PAR_warning(Arg::Warning(isc_illegal_prc_type) << Arg::Str(name)); node->inputTargets = nullptr;
else
PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << Arg::Str(name));
}
ProcedureSourceNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode(
*tdbb->getDefaultPool());
node->procedure = procedure;
node->isSubRoutine = procedure->isSubRoutine();
node->procedureId = node->isSubRoutine ? 0 : procedure->getId();
if (aliasString)
node->alias = *aliasString;
if (parseContext)
{
node->stream = PAR_context(csb, &node->context);
csb->csb_rpt[node->stream].csb_procedure = procedure;
csb->csb_rpt[node->stream].csb_alias = aliasString.release();
PAR_procedure_parms(tdbb, csb, procedure, node->in_msg.getAddress(),
node->sourceList.getAddress(), node->targetList.getAddress(), true);
if (csb->collectingDependencies())
PAR_dependency(tdbb, csb, node->stream, (SSHORT) -1, "");
} }
return node; return node;
@ -1023,7 +1184,7 @@ string ProcedureSourceNode::internalPrint(NodePrinter& printer) const
{ {
RecordSourceNode::internalPrint(printer); RecordSourceNode::internalPrint(printer);
NODE_PRINT(printer, in_msg); NODE_PRINT(printer, inputMessage);
NODE_PRINT(printer, context); NODE_PRINT(printer, context);
return "ProcedureSourceNode"; return "ProcedureSourceNode";
@ -1040,7 +1201,7 @@ bool ProcedureSourceNode::dsqlAggregateFinder(AggregateFinder& visitor)
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
{ {
// Check if an aggregate is buried inside the input parameters. // Check if an aggregate is buried inside the input parameters.
return visitor.visit(dsqlContext->ctx_proc_inputs); return visitor.visit(inputSources);
} }
return false; return false;
@ -1049,7 +1210,7 @@ bool ProcedureSourceNode::dsqlAggregateFinder(AggregateFinder& visitor)
bool ProcedureSourceNode::dsqlAggregate2Finder(Aggregate2Finder& visitor) bool ProcedureSourceNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
{ {
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs); return visitor.visit(inputSources);
return false; return false;
} }
@ -1058,7 +1219,7 @@ bool ProcedureSourceNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& vis
{ {
// If relation is a procedure, check if the parameters are valid. // If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs); return visitor.visit(inputSources);
return false; return false;
} }
@ -1067,7 +1228,7 @@ bool ProcedureSourceNode::dsqlSubSelectFinder(SubSelectFinder& visitor)
{ {
// If relation is a procedure, check if the parameters are valid. // If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs); return visitor.visit(inputSources);
return false; return false;
} }
@ -1076,7 +1237,7 @@ bool ProcedureSourceNode::dsqlFieldFinder(FieldFinder& visitor)
{ {
// If relation is a procedure, check if the parameters are valid. // If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs); return visitor.visit(inputSources);
return false; return false;
} }
@ -1085,7 +1246,7 @@ RecordSourceNode* ProcedureSourceNode::dsqlFieldRemapper(FieldRemapper& visitor)
{ {
// Check if relation is a procedure. // Check if relation is a procedure.
if (dsqlContext->ctx_procedure) if (dsqlContext->ctx_procedure)
doDsqlFieldRemapper(visitor, dsqlContext->ctx_proc_inputs); // Remap the input parameters. doDsqlFieldRemapper(visitor, inputSources); // Remap the input parameters.
return this; return this;
} }
@ -1100,12 +1261,67 @@ bool ProcedureSourceNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const Expr
// Generate blr for a procedure reference. // Generate blr for a procedure reference.
void ProcedureSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch) void ProcedureSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{ {
const dsql_prc* procedure = dsqlContext->ctx_procedure; const dsql_prc* dsqlProcedure = dsqlContext->ctx_procedure;
if (procedure->prc_flags & PRC_subproc) if (dsqlInputArgNames)
{
dsqlScratch->appendUChar(blr_select_procedure);
dsqlScratch->appendUChar(blr_invsel_procedure_type);
if (dsqlName.package.hasData())
{
dsqlScratch->appendUChar(blr_invsel_procedure_type_packaged);
dsqlScratch->appendMetaString(dsqlName.package.c_str());
}
else
{
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ?
blr_invsel_procedure_type_sub : blr_invsel_procedure_type_standalone);
}
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
// Input parameters.
if (inputSources)
{
if (dsqlInputArgNames && dsqlInputArgNames->hasData())
{
dsqlScratch->appendUChar(blr_invsel_procedure_in_arg_names);
dsqlScratch->appendUShort(dsqlInputArgNames->getCount());
for (auto& argName : *dsqlInputArgNames)
dsqlScratch->appendMetaString(argName.c_str());
}
dsqlScratch->appendUChar(blr_invsel_procedure_in_args);
dsqlScratch->appendUShort(inputSources->items.getCount());
for (auto& arg : inputSources->items)
GEN_expr(dsqlScratch, arg);
}
if (dsqlContext->ctx_context > MAX_UCHAR)
ERRD_post(Arg::Gds(isc_too_many_contexts));
dsqlScratch->appendUChar(blr_invsel_procedure_context);
dsqlScratch->appendUShort(dsqlContext->ctx_context);
if (dsqlContext->ctx_alias.hasData())
{
dsqlScratch->appendUChar(blr_invsel_procedure_alias);
dsqlScratch->appendMetaString(dsqlContext->ctx_alias.c_str());
}
dsqlScratch->appendUChar(blr_end);
return;
}
if (dsqlProcedure->prc_flags & PRC_subproc)
{ {
dsqlScratch->appendUChar(blr_subproc); dsqlScratch->appendUChar(blr_subproc);
dsqlScratch->appendMetaString(procedure->prc_name.identifier.c_str()); dsqlScratch->appendMetaString(dsqlProcedure->prc_name.identifier.c_str());
dsqlScratch->appendMetaString(dsqlContext->ctx_alias.c_str()); dsqlScratch->appendMetaString(dsqlContext->ctx_alias.c_str());
} }
else else
@ -1115,20 +1331,20 @@ void ProcedureSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
if (DDL_ids(dsqlScratch)) if (DDL_ids(dsqlScratch))
{ {
dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_pid2 : blr_pid); dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_pid2 : blr_pid);
dsqlScratch->appendUShort(procedure->prc_id); dsqlScratch->appendUShort(dsqlProcedure->prc_id);
} }
else else
{ {
if (procedure->prc_name.package.hasData()) if (dsqlProcedure->prc_name.package.hasData())
{ {
dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_procedure4 : blr_procedure3); dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_procedure4 : blr_procedure3);
dsqlScratch->appendMetaString(procedure->prc_name.package.c_str()); dsqlScratch->appendMetaString(dsqlProcedure->prc_name.package.c_str());
dsqlScratch->appendMetaString(procedure->prc_name.identifier.c_str()); dsqlScratch->appendMetaString(dsqlProcedure->prc_name.identifier.c_str());
} }
else else
{ {
dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_procedure2 : blr_procedure); dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_procedure2 : blr_procedure);
dsqlScratch->appendMetaString(procedure->prc_name.identifier.c_str()); dsqlScratch->appendMetaString(dsqlProcedure->prc_name.identifier.c_str());
} }
} }
@ -1138,18 +1354,12 @@ void ProcedureSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
GEN_stuff_context(dsqlScratch, dsqlContext); GEN_stuff_context(dsqlScratch, dsqlContext);
ValueListNode* inputs = dsqlContext->ctx_proc_inputs; if (inputSources && !(dsqlFlags & DFLAG_PLAN_ITEM))
if (inputs && !(dsqlFlags & DFLAG_PLAN_ITEM))
{ {
dsqlScratch->appendUShort(inputs->items.getCount()); dsqlScratch->appendUShort(inputSources->items.getCount());
for (NestConst<ValueExprNode>* ptr = inputs->items.begin(); for (auto& arg : inputSources->items)
ptr != inputs->items.end(); GEN_expr(dsqlScratch, arg);
++ptr)
{
GEN_expr(dsqlScratch, *ptr);
}
} }
else else
dsqlScratch->appendUShort(0); dsqlScratch->appendUShort(0);
@ -1180,12 +1390,12 @@ ProcedureSourceNode* ProcedureSourceNode::copy(thread_db* tdbb, NodeCopier& copi
// dimitr: See the appropriate code and comment in NodeCopier (in nod_argument). // dimitr: See the appropriate code and comment in NodeCopier (in nod_argument).
// We must copy the message first and only then use the new pointer to // We must copy the message first and only then use the new pointer to
// copy the inputs properly. // copy the inputs properly.
newSource->in_msg = copier.copy(tdbb, in_msg); newSource->inputMessage = copier.copy(tdbb, inputMessage);
{ // scope { // scope
AutoSetRestore<MessageNode*> autoMessage(&copier.message, newSource->in_msg); AutoSetRestore<MessageNode*> autoMessage(&copier.message, newSource->inputMessage);
newSource->sourceList = copier.copy(tdbb, sourceList); newSource->inputSources = copier.copy(tdbb, inputSources);
newSource->targetList = copier.copy(tdbb, targetList); newSource->inputTargets = copier.copy(tdbb, inputTargets);
} }
newSource->stream = copier.csb->nextStream(); newSource->stream = copier.csb->nextStream();
@ -1211,9 +1421,9 @@ ProcedureSourceNode* ProcedureSourceNode::copy(thread_db* tdbb, NodeCopier& copi
RecordSourceNode* ProcedureSourceNode::pass1(thread_db* tdbb, CompilerScratch* csb) RecordSourceNode* ProcedureSourceNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{ {
doPass1(tdbb, csb, sourceList.getAddress()); doPass1(tdbb, csb, inputSources.getAddress());
doPass1(tdbb, csb, targetList.getAddress()); doPass1(tdbb, csb, inputTargets.getAddress());
doPass1(tdbb, csb, in_msg.getAddress()); doPass1(tdbb, csb, inputMessage.getAddress());
return this; return this;
} }
@ -1256,9 +1466,9 @@ void ProcedureSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse
RecordSourceNode* ProcedureSourceNode::pass2(thread_db* tdbb, CompilerScratch* csb) RecordSourceNode* ProcedureSourceNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{ {
ExprNode::doPass2(tdbb, csb, sourceList.getAddress()); ExprNode::doPass2(tdbb, csb, inputSources.getAddress());
ExprNode::doPass2(tdbb, csb, targetList.getAddress()); ExprNode::doPass2(tdbb, csb, inputTargets.getAddress());
ExprNode::doPass2(tdbb, csb, in_msg.getAddress()); ExprNode::doPass2(tdbb, csb, inputMessage.getAddress());
return this; return this;
} }
@ -1275,16 +1485,16 @@ RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool
const string alias = opt->makeAlias(stream); const string alias = opt->makeAlias(stream);
return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(csb, alias, stream, procedure, return FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureScan(csb, alias, stream, procedure,
sourceList, targetList, in_msg); inputSources, inputTargets, inputMessage);
} }
bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream, bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream,
bool allowOnlyCurrentStream, ValueExprNode* /*value*/) bool allowOnlyCurrentStream, ValueExprNode* /*value*/)
{ {
if (sourceList && !sourceList->computable(csb, stream, allowOnlyCurrentStream)) if (inputSources && !inputSources->computable(csb, stream, allowOnlyCurrentStream))
return false; return false;
if (targetList && !targetList->computable(csb, stream, allowOnlyCurrentStream)) if (inputTargets && !inputTargets->computable(csb, stream, allowOnlyCurrentStream))
return false; return false;
return true; return true;
@ -1293,22 +1503,22 @@ bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream,
void ProcedureSourceNode::findDependentFromStreams(const CompilerScratch* csb, void ProcedureSourceNode::findDependentFromStreams(const CompilerScratch* csb,
StreamType currentStream, SortedStreamList* streamList) StreamType currentStream, SortedStreamList* streamList)
{ {
if (sourceList) if (inputSources)
sourceList->findDependentFromStreams(csb, currentStream, streamList); inputSources->findDependentFromStreams(csb, currentStream, streamList);
if (targetList) if (inputTargets)
targetList->findDependentFromStreams(csb, currentStream, streamList); inputTargets->findDependentFromStreams(csb, currentStream, streamList);
} }
void ProcedureSourceNode::collectStreams(SortedStreamList& streamList) const void ProcedureSourceNode::collectStreams(SortedStreamList& streamList) const
{ {
RecordSourceNode::collectStreams(streamList); RecordSourceNode::collectStreams(streamList);
if (sourceList) if (inputSources)
sourceList->collectStreams(streamList); inputSources->collectStreams(streamList);
if (targetList) if (inputTargets)
targetList->collectStreams(streamList); inputTargets->collectStreams(streamList);
} }
@ -1706,7 +1916,7 @@ UnionSourceNode* UnionSourceNode::parse(thread_db* tdbb, CompilerScratch* csb, c
if (node->recursive) if (node->recursive)
{ {
stream2 = PAR_context(csb, 0); stream2 = PAR_context(csb, nullptr);
node->mapStream = stream2; node->mapStream = stream2;
} }
@ -3452,13 +3662,13 @@ static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, Recor
MetaName relName; MetaName relName;
string relAlias; string relAlias;
if (auto procNode = nodeAs<ProcedureSourceNode>(source)) if (const auto procNode = nodeAs<ProcedureSourceNode>(source))
{ {
relName = procNode->dsqlName.identifier; relName = procNode->dsqlName.identifier;
relAlias = procNode->alias; relAlias = procNode->alias;
couldBeCte = !procNode->sourceList && procNode->dsqlName.package.isEmpty(); couldBeCte = !procNode->inputSources && procNode->dsqlName.package.isEmpty();
} }
else if (auto relNode = nodeAs<RelationSourceNode>(source)) else if (const auto relNode = nodeAs<RelationSourceNode>(source))
{ {
relName = relNode->dsqlName; relName = relNode->dsqlName;
relAlias = relNode->alias; relAlias = relNode->alias;

View File

@ -434,21 +434,14 @@ public:
const QualifiedName& aDsqlName = QualifiedName()) const QualifiedName& aDsqlName = QualifiedName())
: TypedNode<RecordSourceNode, RecordSourceNode::TYPE_PROCEDURE>(pool), : TypedNode<RecordSourceNode, RecordSourceNode::TYPE_PROCEDURE>(pool),
dsqlName(pool, aDsqlName), dsqlName(pool, aDsqlName),
alias(pool), alias(pool)
procedure(NULL),
sourceList(NULL),
targetList(NULL),
in_msg(NULL),
view(NULL),
procedureId(0),
context(0),
isSubRoutine(false)
{ {
} }
static ProcedureSourceNode* parse(thread_db* tdbb, CompilerScratch* csb, const SSHORT blrOp, static ProcedureSourceNode* parse(thread_db* tdbb, CompilerScratch* csb, const SSHORT blrOp,
bool parseContext); bool parseContext);
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const; virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual RecordSourceNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); virtual RecordSourceNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
@ -510,17 +503,18 @@ public:
cache management policies yet, so I leave it for the other day. cache management policies yet, so I leave it for the other day.
***/ ***/
jrd_prc* procedure; jrd_prc* procedure = nullptr;
NestConst<ValueListNode> sourceList; NestConst<ValueListNode> inputSources;
NestConst<ValueListNode> targetList; NestConst<ValueListNode> inputTargets;
NestConst<Firebird::ObjectsArray<MetaName>> dsqlInputArgNames;
private: private:
NestConst<MessageNode> in_msg; NestConst<MessageNode> inputMessage;
jrd_rel* view; jrd_rel* view = nullptr;
USHORT procedureId; USHORT procedureId = 0;
SSHORT context; SSHORT context = 0;
bool isSubRoutine; bool isSubRoutine = false;
}; };
class AggregateSourceNode final : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_AGGREGATE_SOURCE> class AggregateSourceNode final : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_AGGREGATE_SOURCE>

View File

@ -255,5 +255,8 @@ static const struct
{"outer_map", outer_map}, {"outer_map", outer_map},
{NULL, NULL}, // blr_json_function {NULL, NULL}, // blr_json_function
{"skip_locked", zero}, {"skip_locked", zero},
{"invoke_function", invoke_function},
{"invoke_procedure", invsel_procedure},
{"select_procedure", invsel_procedure},
{0, 0} {0, 0}
}; };

View File

@ -459,6 +459,173 @@ ItemInfo* CMP_pass2_validation(thread_db* tdbb, CompilerScratch* csb, const Item
} }
Arg::StatusVector CMP_procedure_arguments(
thread_db* tdbb,
CompilerScratch* csb,
Routine* routine,
bool isInput,
USHORT argCount,
ObjectsArray<MetaName>* argNames,
NestConst<ValueListNode>& sources,
NestConst<ValueListNode>& targets,
NestConst<MessageNode>& message)
{
Arg::StatusVector mismatchStatus;
auto& pool = *tdbb->getDefaultPool();
auto& fields = isInput ? routine->getInputFields() : routine->getOutputFields();
const auto format = isInput ? routine->getInputFormat() : routine->getOutputFormat();
if ((isInput && fields.hasData() && (argCount || routine->getDefaultCount())) ||
(!isInput && argCount))
{
if (isInput)
sources->items.resize(fields.getCount());
else
targets = FB_NEW_POOL(pool) ValueListNode(pool, argCount);
auto sourceArgIt = sources->items.begin();
auto targetArgIt = targets->items.begin();
// 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;
const auto tail = CMP_csb_element(csb, n);
message = tail->csb_message = FB_NEW_POOL(pool) MessageNode(pool);
message->messageNumber = n;
/* dimitr: procedure (with its parameter formats) is allocated out of
its own pool (prc_request->req_pool) and can be freed during
the cache cleanup (MET_clear_cache). Since the current
tdbb default pool is different from the procedure's one,
it's dangerous to copy a pointer from one request to another.
As an experiment, I've decided to copy format by value
instead of copying the reference. Since Format structure
doesn't contain any pointers, it should be safe to use a
default assignment operator which does a simple byte copy.
This change fixes one serious bug in the current codebase.
I think that this situation can (and probably should) be
handled by the metadata cache (via incrementing prc_use_count)
to avoid unexpected cache cleanups, but that area is out of my
knowledge. So this fix should be considered a temporary solution.
message->format = format;
*/
const auto fmtCopy = Format::newFormat(pool, format->fmt_count);
*fmtCopy = *format;
message->format = fmtCopy;
// --- end of fix ---
if (argNames)
{
LeftPooledMap<MetaName, NestConst<ValueExprNode>> argsByName;
for (const auto& argName : *argNames)
{
if (argsByName.put(argName, *sourceArgIt++))
mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << argName;
}
sourceArgIt = sources->items.begin();
for (auto& parameter : fields)
{
if (const auto argValue = argsByName.get(parameter->prm_name))
{
*sourceArgIt = *argValue;
argsByName.remove(parameter->prm_name);
}
else if (isInput)
{
if (parameter->prm_default_value)
*sourceArgIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
else
mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name;
}
++sourceArgIt;
const auto paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramNode->messageNumber = message->messageNumber;
paramNode->message = message;
paramNode->argNumber = parameter->prm_number * 2;
const auto paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramFlagNode->messageNumber = message->messageNumber;
paramFlagNode->message = message;
paramFlagNode->argNumber = parameter->prm_number * 2 + 1;
paramNode->argFlag = paramFlagNode;
*targetArgIt++ = paramNode;
}
if (argsByName.hasData())
{
for (const auto& argPair : argsByName)
mismatchStatus << Arg::Gds(isc_param_not_exist) << argPair.first;
}
}
else
{
for (unsigned i = 0; i < (isInput ? fields.getCount() : argCount); ++i)
{
if (isInput)
{
// default value for parameter
if (i >= argCount)
{
auto parameter = fields[i];
if (parameter->prm_default_value)
*sourceArgIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
}
++sourceArgIt;
}
const auto paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramNode->messageNumber = message->messageNumber;
paramNode->message = message;
paramNode->argNumber = i * 2;
const auto paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramFlagNode->messageNumber = message->messageNumber;
paramFlagNode->message = message;
paramFlagNode->argNumber = i * 2 + 1;
paramNode->argFlag = paramFlagNode;
*targetArgIt++ = paramNode;
}
}
}
if (isInput && !argNames)
{
if (argCount > fields.getCount())
mismatchStatus << Arg::Gds(isc_wronumarg);
for (unsigned i = 0; i < fields.getCount(); ++i)
{
// default value for parameter
if (i >= argCount)
{
auto parameter = fields[i];
if (!parameter->prm_default_value)
mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name;
}
}
}
return mismatchStatus;
}
void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure) void CMP_post_procedure_access(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure)
{ {
/************************************** /**************************************

View File

@ -46,6 +46,17 @@ Jrd::IndexLock* CMP_get_index_lock(Jrd::thread_db*, Jrd::jrd_rel*, USHORT);
Jrd::Request* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool); Jrd::Request* CMP_make_request(Jrd::thread_db*, Jrd::CompilerScratch*, bool);
Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&); Jrd::ItemInfo* CMP_pass2_validation(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::Item&);
Firebird::Arg::StatusVector CMP_procedure_arguments(
Jrd::thread_db* tdbb,
Jrd::CompilerScratch* csb,
Jrd::Routine* routine,
bool isInput,
USHORT argCount,
Firebird::ObjectsArray<Jrd::MetaName>* argNames,
NestConst<Jrd::ValueListNode>& sources,
NestConst<Jrd::ValueListNode>& targets,
NestConst<Jrd::MessageNode>& message);
void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName&, SLONG ssRelationId, void CMP_post_access(Jrd::thread_db*, Jrd::CompilerScratch*, const Jrd::MetaName&, SLONG ssRelationId,
Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&, Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&,
const Jrd::MetaName& = ""); const Jrd::MetaName& = "");

View File

@ -859,7 +859,7 @@ namespace
return true; return true;
case 6: case 6:
Self::checkOutParamDependencies(tdbb, work, transaction); Self::checkParamDependencies(tdbb, work, transaction);
break; break;
} }
@ -1076,7 +1076,7 @@ namespace
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr); SSHORT validBlr);
static void checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction);
}; };
class ProcedureManager : public RoutineManager<ProcedureManager, jrd_prc, obj_procedure, class ProcedureManager : public RoutineManager<ProcedureManager, jrd_prc, obj_procedure,
@ -1096,7 +1096,7 @@ namespace
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
SSHORT validBlr); SSHORT validBlr);
static void checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction);
}; };
// These methods cannot be defined inline, because GPRE generates wrong code. // These methods cannot be defined inline, because GPRE generates wrong code.
@ -1141,9 +1141,48 @@ namespace
END_FOR END_FOR
} }
void FunctionManager::checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction) void FunctionManager::checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction)
{ {
// Do nothing, as function output is unnamed. Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest handle(tdbb, irq_func_param_dep, IRQ_REQUESTS);
ObjectsArray<string> names;
int depCount = 0;
FOR (REQUEST_HANDLE handle)
DEP IN RDB$DEPENDENCIES
WITH DEP.RDB$DEPENDED_ON_NAME EQ work->dfw_name.c_str() AND
DEP.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') AND
DEP.RDB$DEPENDED_ON_TYPE = obj_udf AND
NOT DEP.RDB$FIELD_NAME MISSING AND
NOT ANY ARG IN RDB$FUNCTION_ARGUMENTS
WITH ARG.RDB$FUNCTION_NAME EQ DEP.RDB$DEPENDED_ON_NAME AND
ARG.RDB$PACKAGE_NAME EQUIV DEP.RDB$PACKAGE_NAME AND
ARG.RDB$ARGUMENT_NAME EQ DEP.RDB$FIELD_NAME
{
// If the found object is also being deleted, there's no dependency
if (!find_depend_in_dfw(tdbb, DEP.RDB$DEPENDENT_NAME, DEP.RDB$DEPENDENT_TYPE, 0, transaction))
{
string& name = names.add();
name.printf("%s.%s", work->dfw_name.c_str(), DEP.RDB$FIELD_NAME);
++depCount;
}
}
END_FOR
if (names.hasData())
{
Arg::StatusVector status;
status << Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_no_delete);
for (auto& name : names)
status << Arg::Gds(isc_parameter_name) << Arg::Str(name);
status << Arg::Gds(isc_dependency) << Arg::Num(depCount);
ERR_post(status);
}
} }
Routine* ProcedureManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, Routine* ProcedureManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId,
@ -1186,10 +1225,10 @@ namespace
END_FOR END_FOR
} }
void ProcedureManager::checkOutParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction) void ProcedureManager::checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction)
{ {
Jrd::Attachment* attachment = tdbb->getAttachment(); Jrd::Attachment* attachment = tdbb->getAttachment();
AutoCacheRequest handle(tdbb, irq_out_proc_param_dep, IRQ_REQUESTS); AutoCacheRequest handle(tdbb, irq_proc_param_dep, IRQ_REQUESTS);
ObjectsArray<string> names; ObjectsArray<string> names;
int depCount = 0; int depCount = 0;
@ -1202,8 +1241,7 @@ namespace
NOT ANY PP IN RDB$PROCEDURE_PARAMETERS NOT ANY PP IN RDB$PROCEDURE_PARAMETERS
WITH PP.RDB$PROCEDURE_NAME EQ DEP.RDB$DEPENDED_ON_NAME AND WITH PP.RDB$PROCEDURE_NAME EQ DEP.RDB$DEPENDED_ON_NAME AND
PP.RDB$PACKAGE_NAME EQUIV DEP.RDB$PACKAGE_NAME AND PP.RDB$PACKAGE_NAME EQUIV DEP.RDB$PACKAGE_NAME AND
PP.RDB$PARAMETER_NAME EQ DEP.RDB$FIELD_NAME AND PP.RDB$PARAMETER_NAME EQ DEP.RDB$FIELD_NAME
PP.RDB$PARAMETER_TYPE EQ 1
{ {
// If the found object is also being deleted, there's no dependency // If the found object is also being deleted, there's no dependency

View File

@ -182,7 +182,8 @@ enum irq_type_t
irq_c_relation3, // lookup relation in phase 0 to cleanup irq_c_relation3, // lookup relation in phase 0 to cleanup
irq_linger, // get database linger value irq_linger, // get database linger value
irq_dbb_ss_definer, // get database sql security value irq_dbb_ss_definer, // get database sql security value
irq_out_proc_param_dep, // check output procedure parameter dependency irq_proc_param_dep, // check procedure parameter dependency
irq_func_param_dep, // check function parameter dependency
irq_l_pub_tab_state, // lookup publication state for a table irq_l_pub_tab_state, // lookup publication state for a table
irq_MAX irq_MAX

View File

@ -821,28 +821,11 @@ ValueListNode* PAR_args(thread_db* tdbb, CompilerScratch* csb)
} }
StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr) // Introduce a new context into the system.
// This involves assigning a stream and possibly extending the compile scratch block.
StreamType par_context(CompilerScratch* csb, USHORT context)
{ {
/************************************** const auto tail = CMP_csb_element(csb, context);
*
* P A R _ c o n t e x t
*
**************************************
*
* Functional description
* Introduce a new context into the system. This involves
* assigning a stream and possibly extending the compile
* scratch block.
*
**************************************/
// CVC: Bottleneck
const SSHORT context = (unsigned int) csb->csb_blr_reader.getByte();
if (context_ptr)
*context_ptr = context;
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, context);
if (tail->csb_flags & csb_used) if (tail->csb_flags & csb_used)
{ {
@ -867,6 +850,28 @@ StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
return stream; return stream;
} }
// Introduce a new context into the system - byte version.
StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
{
const USHORT context = csb->csb_blr_reader.getByte();
if (context_ptr)
*context_ptr = (SSHORT) context;
return par_context(csb, context);
}
// Introduce a new context into the system - word version.
StreamType PAR_context2(CompilerScratch* csb, SSHORT* context_ptr)
{
const USHORT context = csb->csb_blr_reader.getWord();
if (context_ptr)
*context_ptr = (SSHORT) context;
return par_context(csb, context);
}
void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SSHORT id, void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SSHORT id,
const MetaName& field_name) const MetaName& field_name)
@ -975,12 +980,12 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
case blr_rid: case blr_rid:
case blr_relation2: case blr_relation2:
case blr_rid2: case blr_rid2:
{ {
const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false); const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false);
plan->recordSourceNode = relationNode; plan->recordSourceNode = relationNode;
relation = relationNode->relation; relation = relationNode->relation;
}
break; break;
}
case blr_pid: case blr_pid:
case blr_pid2: case blr_pid2:
@ -989,12 +994,13 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
case blr_procedure3: case blr_procedure3:
case blr_procedure4: case blr_procedure4:
case blr_subproc: case blr_subproc:
{ case blr_select_procedure:
const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false); {
plan->recordSourceNode = procedureNode; const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false);
procedure = procedureNode->procedure; plan->recordSourceNode = procedureNode;
} procedure = procedureNode->procedure;
break; break;
}
case blr_local_table_id: case blr_local_table_id:
// TODO // TODO
@ -1010,7 +1016,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
const StreamType stream = csb->csb_rpt[context].csb_stream; const StreamType stream = csb->csb_rpt[context].csb_stream;
plan->recordSourceNode->setStream(stream); plan->recordSourceNode->setStream(stream);
// plan->recordSourceNode->context = context; not needed ??? /// plan->recordSourceNode->context = context; not needed ???
if (procedure) if (procedure)
{ {
@ -1167,104 +1173,6 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
} }
// Parse some procedure parameters.
void PAR_procedure_parms(thread_db* tdbb, CompilerScratch* csb, jrd_prc* procedure,
MessageNode** message_ptr, ValueListNode** sourceList, ValueListNode** targetList, bool input_flag)
{
SET_TDBB(tdbb);
SLONG count = csb->csb_blr_reader.getWord();
const SLONG inputCount = procedure->getInputFields().getCount();
// Check to see if the parameter count matches
if (input_flag ?
(count < (inputCount - procedure->getDefaultCount()) || (count > inputCount) ) :
(count != SLONG(procedure->getOutputFields().getCount())))
{
PAR_error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->getName().toString()));
}
if (count || input_flag && procedure->getDefaultCount())
{
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;
CompilerScratch::csb_repeat* tail = CMP_csb_element(csb, n);
MessageNode* message = tail->csb_message = *message_ptr = FB_NEW_POOL(pool) MessageNode(pool);
message->messageNumber = n;
const Format* format = input_flag ? procedure->getInputFormat() : procedure->getOutputFormat();
/* dimitr: procedure (with its parameter formats) is allocated out of
its own pool (prc_request->req_pool) and can be freed during
the cache cleanup (MET_clear_cache). Since the current
tdbb default pool is different from the procedure's one,
it's dangerous to copy a pointer from one request to another.
As an experiment, I've decided to copy format by value
instead of copying the reference. Since Format structure
doesn't contain any pointers, it should be safe to use a
default assignment operator which does a simple byte copy.
This change fixes one serious bug in the current codebase.
I think that this situation can (and probably should) be
handled by the metadata cache (via incrementing prc_use_count)
to avoid unexpected cache cleanups, but that area is out of my
knowledge. So this fix should be considered a temporary solution.
message->format = format;
*/
Format* fmt_copy = Format::newFormat(pool, format->fmt_count);
*fmt_copy = *format;
message->format = fmt_copy;
// --- end of fix ---
n = format->fmt_count / 2;
ValueListNode* sourceValues = *sourceList = FB_NEW_POOL(pool) ValueListNode(pool, n);
ValueListNode* targetValues = *targetList = FB_NEW_POOL(pool) ValueListNode(pool, n);
NestConst<ValueExprNode>* sourcePtr =
input_flag ? sourceValues->items.begin() : targetValues->items.begin();
NestConst<ValueExprNode>* targetPtr =
input_flag ? targetValues->items.begin() : sourceValues->items.begin();
for (USHORT i = 0; n; count--, n--)
{
// default value for parameter
if (count <= 0 && input_flag)
{
Parameter* parameter = procedure->getInputFields()[inputCount - n];
*sourcePtr++ = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
}
else
*sourcePtr++ = PAR_parse_value(tdbb, csb);
ParameterNode* paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramNode->messageNumber = message->messageNumber;
paramNode->message = message;
paramNode->argNumber = i++;
ParameterNode* paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool);
paramFlagNode->messageNumber = message->messageNumber;
paramFlagNode->message = message;
paramFlagNode->argNumber = i++;
paramNode->argFlag = paramFlagNode;
*targetPtr++ = paramNode;
}
}
else if (input_flag ? inputCount : procedure->getOutputFields().getCount())
{
PAR_error(csb, Arg::Gds(input_flag ? isc_prcmismat : isc_prc_out_param_mismatch) <<
Arg::Str(procedure->getName().toString()));
}
}
// Parse a RecordSourceNode. // Parse a RecordSourceNode.
RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb) RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb)
{ {
@ -1281,6 +1189,7 @@ RecordSourceNode* PAR_parseRecordSource(thread_db* tdbb, CompilerScratch* csb)
case blr_procedure3: case blr_procedure3:
case blr_procedure4: case blr_procedure4:
case blr_subproc: case blr_subproc:
case blr_select_procedure:
return ProcedureSourceNode::parse(tdbb, csb, blrOp, true); return ProcedureSourceNode::parse(tdbb, csb, blrOp, true);
case blr_rse: case blr_rse:
@ -1638,6 +1547,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
case blr_procedure3: case blr_procedure3:
case blr_procedure4: case blr_procedure4:
case blr_subproc: case blr_subproc:
case blr_select_procedure:
case blr_relation: case blr_relation:
case blr_rid: case blr_rid:
case blr_relation2: case blr_relation2:

View File

@ -54,6 +54,7 @@ void PAR_preparsed_node(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::DmlNode*,
Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR* blr, Jrd::BoolExprNode* PAR_validation_blr(Jrd::thread_db*, Jrd::jrd_rel*, const UCHAR* blr,
ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT); ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT);
StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*); StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*);
StreamType PAR_context2(Jrd::CompilerScratch*, SSHORT*);
void PAR_dependency(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, StreamType stream, void PAR_dependency(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, StreamType stream,
SSHORT id, const Jrd::MetaName& field_name); SSHORT id, const Jrd::MetaName& field_name);
USHORT PAR_datatype(Firebird::BlrReader&, dsc*); USHORT PAR_datatype(Firebird::BlrReader&, dsc*);
@ -68,8 +69,6 @@ Jrd::CompilerScratch* PAR_parse(Jrd::thread_db*, const UCHAR* blr, ULONG blr_len
bool internal_flag, ULONG = 0, const UCHAR* = NULL); bool internal_flag, ULONG = 0, const UCHAR* = NULL);
Jrd::RecordSourceNode* PAR_parseRecordSource(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb); Jrd::RecordSourceNode* PAR_parseRecordSource(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb);
void PAR_procedure_parms(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::jrd_prc*,
Jrd::MessageNode**, Jrd::ValueListNode**, Jrd::ValueListNode**, bool input_flag);
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*, SSHORT); Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*, SSHORT);
Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*); Jrd::RseNode* PAR_rse(Jrd::thread_db*, Jrd::CompilerScratch*);
Jrd::SortNode* PAR_sort(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR, bool); Jrd::SortNode* PAR_sort(Jrd::thread_db*, Jrd::CompilerScratch*, UCHAR, bool);

View File

@ -235,6 +235,7 @@ static void blr_print_cond(gds_ctl*, SSHORT);
static int blr_print_dtype(gds_ctl*); static int blr_print_dtype(gds_ctl*);
static void blr_print_join(gds_ctl*); static void blr_print_join(gds_ctl*);
static SLONG blr_print_line(gds_ctl*, SSHORT); static SLONG blr_print_line(gds_ctl*, SSHORT);
static void blr_print_name(gds_ctl*);
static void blr_print_verb(gds_ctl*, SSHORT); static void blr_print_verb(gds_ctl*, SSHORT);
static int blr_print_word(gds_ctl*); static int blr_print_word(gds_ctl*);
@ -323,6 +324,8 @@ const int op_window_win = 29;
const int op_erase = 30; // special due to optional blr_marks after blr_erase const int op_erase = 30; // special due to optional blr_marks after blr_erase
const int op_dcl_local_table = 31; const int op_dcl_local_table = 31;
const int op_outer_map = 32; const int op_outer_map = 32;
const int op_invoke_function = 33;
const int op_invsel_procedure = 34;
static const UCHAR static const UCHAR
// generic print formats // generic print formats
@ -413,7 +416,9 @@ static const UCHAR
erase[] = { op_erase, 0}, erase[] = { op_erase, 0},
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0}, local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 0},
outer_map[] = { op_outer_map, 0 }, outer_map[] = { op_outer_map, 0 },
in_list[] = { op_verb, op_line, op_word, op_line, op_args, 0}; in_list[] = { op_verb, op_line, op_word, op_line, op_args, 0},
invoke_function[] = { op_invoke_function, 0 },
invsel_procedure[] = { op_invsel_procedure, 0 };
#include "../jrd/blp.h" #include "../jrd/blp.h"
@ -3325,6 +3330,15 @@ static SLONG blr_print_line(gds_ctl* control, SSHORT offset)
} }
static void blr_print_name(gds_ctl* control)
{
auto len = blr_print_byte(control);
while (len-- > 0)
blr_print_char(control);
}
static void blr_print_verb(gds_ctl* control, SSHORT level) static void blr_print_verb(gds_ctl* control, SSHORT level)
{ {
/************************************** /**************************************
@ -3938,6 +3952,206 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
break; break;
} }
case op_invoke_function:
{
offset = blr_print_line(control, offset);
static const char* subCodes[] =
{
nullptr,
"type",
"arg_names",
"args"
};
static const char* typeSubCodes[] =
{
nullptr,
"standalone",
"packaged",
"sub"
};
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_invoke_function sub code ***");
blr_format(control, "blr_invoke_function_%s, ", subCodes[blr_operator]);
switch (blr_operator)
{
case blr_invoke_function_type:
n = control->ctl_blr_reader.getByte();
if (n == 0 || n >= FB_NELEM(typeSubCodes))
blr_error(control, "*** invalid blr_invoke_function_type sub code ***");
blr_format(control, "blr_invoke_function_type_%s,", typeSubCodes[n]);
offset = blr_print_line(control, (SSHORT) offset);
blr_indent(control, level + 1);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
if (n == blr_invoke_function_type_packaged)
{
blr_indent(control, level + 1);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
}
break;
case blr_invoke_function_arg_names:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (n-- > 0)
{
blr_indent(control, level);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
}
--level;
break;
case blr_invoke_function_args:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (n-- > 0)
blr_print_verb(control, level);
--level;
break;
default:
fb_assert(false);
}
}
// print blr_end
control->ctl_blr_reader.seekBackward(1);
blr_print_verb(control, level);
break;
}
case op_invsel_procedure:
{
offset = blr_print_line(control, offset);
static const char* subCodes[] =
{
nullptr,
"type",
"in_arg_names",
"in_args",
"out_arg_names",
"out_args",
"context",
"alias"
};
static const char* typeSubCodes[] =
{
nullptr,
"standalone",
"packaged",
"sub"
};
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_invsel_procedure sub code ***");
blr_format(control, "blr_invsel_procedure_%s, ", subCodes[blr_operator]);
switch (blr_operator)
{
case blr_invsel_procedure_type:
n = control->ctl_blr_reader.getByte();
if (n == 0 || n >= FB_NELEM(typeSubCodes))
blr_error(control, "*** invalid blr_invsel_procedure_type sub code ***");
blr_format(control, "blr_invsel_procedure_type_%s,", typeSubCodes[n]);
offset = blr_print_line(control, (SSHORT) offset);
blr_indent(control, level + 1);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
if (n == blr_invsel_procedure_type_packaged)
{
blr_indent(control, level + 1);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
}
break;
case blr_invsel_procedure_in_arg_names:
case blr_invsel_procedure_out_arg_names:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (n-- > 0)
{
blr_indent(control, level);
blr_print_name(control);
offset = blr_print_line(control, (SSHORT) offset);
}
--level;
break;
case blr_invsel_procedure_in_args:
case blr_invsel_procedure_out_args:
n = blr_print_word(control);
offset = blr_print_line(control, offset);
++level;
while (n-- > 0)
blr_print_verb(control, level);
--level;
break;
case blr_invsel_procedure_context:
blr_print_word(control);
offset = blr_print_line(control, offset);
break;
case blr_invsel_procedure_alias:
blr_print_name(control);
offset = blr_print_line(control, offset);
break;
default:
fb_assert(false);
}
}
// print blr_end
control->ctl_blr_reader.seekBackward(1);
blr_print_verb(control, level);
break;
}
default: default:
fb_assert(false); fb_assert(false);
break; break;