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_NEQ, "<>", false) // Alias of !=
PARSER_TOKEN('=', "=", false)
PARSER_TOKEN(TOK_NAMED_ARG_ASSIGN, "=>", false)
PARSER_TOKEN('>', ">", false)
PARSER_TOKEN(TOK_GEQ, ">=", 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),
name(pool, aName),
args(aArgs),
function(NULL),
dsqlFunction(NULL),
isSubRoutine(false)
dsqlArgNames(aDsqlArgNames)
{
}
DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
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;
if (blrOp == blr_function2)
csb->csb_blr_reader.getMetaName(name.package);
const auto node = FB_NEW_POOL(pool) UdfCallNode(pool);
csb->csb_blr_reader.getMetaName(name.identifier);
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"))
if (blrOp == blr_invoke_function)
{
csb->csb_blr_reader.setPos(savePos);
return SysFuncCallNode::parse(tdbb, pool, csb, blr_sys_function);
}
UCHAR subCode;
if (blrOp == blr_subfunc)
{
DeclareSubFuncNode* declareNode;
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
while ((subCode = blrReader.getByte()) != blr_end)
{
if (curCsb->subFunctions.get(name.identifier, declareNode))
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))
switch (subCode)
{
PAR_warning(Arg::Warning(isc_funnotdef) << Arg::Str(name.toString()) <<
Arg::Warning(isc_modnotfound));
}
else
{
csb->csb_blr_reader.seekBackward(count);
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) <<
Arg::Gds(isc_modnotfound));
case blr_invoke_function_type:
{
UCHAR functionType = blrReader.getByte();
switch (functionType)
{
case blr_invoke_function_type_packaged:
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
{
csb->csb_blr_reader.seekBackward(count);
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()));
if (blrOp == blr_function2)
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();
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)
if (!node->function)
{
Parameter* const parameter = function->getInputFields()[i];
node->args->items[i] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
blrReader.setPos(startPos);
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.
if (!function->isSubRoutine() && csb->collectingDependencies())
if (!node->function->isSubRoutine() && csb->collectingDependencies())
{
CompilerScratch::Dependency dependency(obj_udf);
dependency.function = function;
csb->addDependency(dependency);
{ // scope
CompilerScratch::Dependency dependency(obj_udf);
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;
@ -12992,6 +13165,45 @@ void UdfCallNode::setParameterName(dsql_par* parameter) const
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())
dsqlScratch->appendUChar((dsqlFunction->udf_flags & UDF_subfunc) ? blr_subfunc : blr_function);
else
@ -13355,8 +13567,11 @@ dsc* UdfCallNode::execute(thread_db* tdbb, Request* request) const
ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
{
UdfCallNode* node = FB_NEW_POOL(dsqlScratch->getPool()) UdfCallNode(dsqlScratch->getPool(), name,
doDsqlPass(dsqlScratch, args));
const auto node = FB_NEW_POOL(dsqlScratch->getPool()) UdfCallNode(dsqlScratch->getPool(), name,
doDsqlPass(dsqlScratch, args),
dsqlArgNames ?
FB_NEW_POOL(dsqlScratch->getPool()) ObjectsArray<MetaName>(dsqlScratch->getPool(), *dsqlArgNames) :
nullptr);
if (name.package.isEmpty())
{
@ -13374,28 +13589,56 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
Arg::Gds(isc_random) << Arg::Str(name.toString()));
}
const USHORT arg_count = node->dsqlFunction->udf_arguments.getCount();
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 (node->dsqlArgNames)
{
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,
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos]; },
false);
}
else
{
// We should complain here in the future! The parameter is
// out of bounds or the function doesn't declare input params.
if (const auto argDescPtr = argsByName.get(argName))
{
PASS1_set_parameter_type(dsqlScratch, *argIt,
[&] (dsc* desc) { *desc = **argDescPtr; },
false);
}
else
{
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;

View File

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

View File

@ -1270,6 +1270,12 @@ public:
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
{
ListExprNode::getChildren(holder, dsql);

View File

@ -1664,8 +1664,8 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
if (!implemetingForward)
{
// ASF: dsqlFunction->udf_arguments is only checked for its count for now.
dsqlFunction->udf_arguments.add(dsc());
// ASF: dsqlFunction->udf_arguments types (desc) are not checked for now.
dsqlFunction->udf_arguments.add().name = param->name;
}
if (param->defaultClause)
@ -2905,79 +2905,304 @@ const StmtNode* ErrorHandlerNode::execute(thread_db* /*tdbb*/, Request* request,
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.
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();
jrd_prc* procedure = NULL;
auto& blrReader = csb->csb_blr_reader;
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;
if (blrOp == blr_exec_pid)
{
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);
const auto node = FB_NEW_POOL(pool) ExecProcedureNode(pool);
csb->csb_blr_reader.getMetaName(name.identifier);
if (blrOp == blr_exec_subproc)
switch (blrOp)
{
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))
procedure = declareNode->routine;
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_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)
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
else
{
if (procedure->isImplemented() && !procedure->isDefined())
break;
}
case blr_exec_pid:
{
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(
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
Arg::Warning(isc_modnotfound));
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
{
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
node->procedure = (*declareNode)->routine;
}
}
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);
PAR_error(csb,
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
Arg::Gds(isc_modnotfound));
CompilerScratch::Dependency dependency(obj_procedure);
dependency.procedure = node->procedure;
dependency.subName = &argName;
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);
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())
if (node->inputSources && node->inputSources->items.isEmpty())
{
CompilerScratch::Dependency dependency(obj_procedure);
dependency.procedure = procedure;
csb->addDependency(dependency);
delete node->inputSources.getObject();
node->inputSources = nullptr;
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;
@ -3007,7 +3232,13 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (!dsqlScratch->isPsql())
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;
if (node->dsqlName.package.isEmpty() && procedure->prc_name.package.hasData())
@ -3015,28 +3246,63 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
// Handle input parameters.
const USHORT count = inputSources ? inputSources->items.getCount() : 0;
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)
if (node->dsqlInputArgNames)
{
// Initialize this stack variable, and make it look like a node.
dsc desc_node;
fb_assert(node->dsqlInputArgNames->getCount() == node->inputSources->items.getCount());
NestConst<ValueExprNode>* ptr = node->inputSources->items.begin();
const NestConst<ValueExprNode>* end = node->inputSources->items.end();
LeftPooledMap<MetaName, const dsql_fld*> argsByName;
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);
DEV_BLKCHK(*ptr, dsql_type_nod);
DsqlDescMaker::fromField(&desc_node, field);
PASS1_set_parameter_type(dsqlScratch, *ptr,
[&] (dsc* desc) { *desc = desc_node; },
false);
if (const auto field = argsByName.get(argName))
{
dsc descNode;
DsqlDescMaker::fromField(&descNode, *field);
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)
{
const dsql_msg* message = NULL;
const dsql_msg* message = nullptr;
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->appendMetaString(dsqlName.package.c_str());
dsqlScratch->appendUChar(blr_invoke_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);
}
// 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
{
dsqlScratch->appendUChar(
(dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc);
if (dsqlName.package.hasData())
{
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)
dsqlScratch->appendUChar(blr_end);
}

View File

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

View File

@ -318,6 +318,19 @@ enum prc_flags_vals {
//! User defined function block
class dsql_udf : public pool_alloc<dsql_type_udf>
{
public:
class Argument
{
public:
Argument(MemoryPool& p)
: name(p)
{}
public:
MetaName name;
dsc desc;
};
public:
explicit dsql_udf(MemoryPool& p)
: udf_name(p),
@ -332,7 +345,7 @@ public:
SSHORT udf_character_set_id = 0;
USHORT udf_flags = 0;
QualifiedName udf_name;
Firebird::Array<dsc> udf_arguments;
Firebird::ObjectsArray<Argument> udf_arguments;
bool udf_private = false; // Packaged private function
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++;
}
userFunc->udf_arguments.add(d);
auto& argument = userFunc->udf_arguments.add();
argument.name = X.RDB$ARGUMENT_NAME;
argument.desc = d;
}
}
END_FOR
@ -842,7 +844,9 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat
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
%token <metaNamePtr> ANY_VALUE
%token <metaNamePtr> NAMED_ARG_ASSIGN
// precedence declarations for expression evaluation
@ -761,6 +762,8 @@ using namespace Firebird;
Jrd::DbFileClause* dbFileClause;
Firebird::Array<NestConst<Jrd::DbFileClause> >* dbFilesClause;
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;
Jrd::WindowClause* windowClause;
Jrd::WindowClause::FrameExtent* windowClauseFrameExtent;
@ -3753,16 +3756,28 @@ fetch_scroll($cursorStmtNode)
%type <stmtNode> exec_procedure
exec_procedure
: 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
{ $$ = 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
: /* nothing */ { $$ = NULL; }
| value_list { $$ = $1; }
| '(' value_list ')' { $$ = $2; }
: /* nothing */ { $$ = nullptr; }
| argument_list { $$ = $1; }
| '(' argument_list ')' { $$ = $2; }
;
%type <valueListNode> proc_outputs_opt
@ -6248,38 +6263,42 @@ named_columns_join
table_proc
: symbol_procedure_name table_proc_inputs as_noise symbol_table_alias_name
{
ProcedureSourceNode* node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->sourceList = $2;
const auto node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->inputSources = $2 ? $2->second : nullptr;
node->dsqlInputArgNames = $2 ? $2->first : nullptr;
node->alias = $4->c_str();
$$ = node;
}
| symbol_procedure_name table_proc_inputs
{
ProcedureSourceNode* node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->sourceList = $2;
const auto node = newNode<ProcedureSourceNode>(QualifiedName(*$1));
node->inputSources = $2 ? $2->second : nullptr;
node->dsqlInputArgNames = $2 ? $2->first : nullptr;
$$ = node;
}
| 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));
node->sourceList = $4;
node->inputSources = $4 ? $4->second : nullptr;
node->dsqlInputArgNames = $4 ? $4->first : nullptr;
node->alias = $6->c_str();
$$ = node;
}
| symbol_package_name '.' symbol_procedure_name table_proc_inputs
{
ProcedureSourceNode* node = newNode<ProcedureSourceNode>(
const auto node = newNode<ProcedureSourceNode>(
QualifiedName(*$3, *$1));
node->sourceList = $4;
node->inputSources = $4 ? $4->second : nullptr;
node->dsqlInputArgNames = $4 ? $4->first : nullptr;
$$ = node;
}
;
%type <valueListNode> table_proc_inputs
%type <namedArguments> table_proc_inputs
table_proc_inputs
: /* nothing */ { $$ = NULL; }
| '(' value_list ')' { $$ = $2; }
: /* nothing */ { $$ = nullptr; }
| '(' argument_list ')' { $$ = $2; }
;
%type <relSourceNode> table_name
@ -8580,14 +8599,55 @@ trim_specification
%type <valueExprNode> udf
udf
: symbol_UDF_call_name '(' value_list ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), $3); }
| symbol_UDF_call_name '(' ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), newNode<ValueListNode>(0)); }
| symbol_package_name '.' symbol_UDF_name '(' value_list ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), $5); }
| symbol_package_name '.' symbol_UDF_name '(' ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), newNode<ValueListNode>(0)); }
: symbol_UDF_call_name '(' argument_list_opt ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$1, ""), $3->second, $3->first); }
| symbol_package_name '.' symbol_UDF_name '(' argument_list_opt ')'
{ $$ = newNode<UdfCallNode>(QualifiedName(*$3, *$1), $5->second, $5->first); }
;
%type <namedArguments> argument_list_opt
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

View File

@ -371,19 +371,16 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{
// 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())
{
DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
const auto subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier);
procedure = subProcedure ? subProcedure->dsqlProcedure : NULL;
}
if (!procedure)
{
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch,
procNode->dsqlName);
}
procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch, procNode->dsqlName);
if (!procedure)
{
@ -401,7 +398,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{
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;
}
@ -409,10 +406,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name);
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)
{
@ -433,7 +427,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
}
// 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_procedure = procedure;
@ -535,9 +529,9 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{
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();
}
@ -551,8 +545,8 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode*
{
// Initialize this stack variable, and make it look like a node
dsc desc_node;
ValueListNode* inputList = context->ctx_proc_inputs;
NestConst<ValueExprNode>* input = inputList->items.begin();
auto inputList = context->ctx_proc_inputs;
auto input = inputList->items.begin();
for (dsql_fld* field = procedure->prc_inputs;
input != inputList->items.end();
@ -1677,21 +1671,22 @@ RecordSourceNode* PASS1_relation(DsqlCompilerScratch* dsqlScratch, RecordSourceN
DEV_BLKCHK(dsqlScratch, dsql_type_req);
dsql_ctx* context = PASS1_make_context(dsqlScratch, input);
RecordSourceNode* node = NULL;
const auto context = PASS1_make_context(dsqlScratch, input);
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);
relNode->dsqlContext = context;
return relNode;
}
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);
procNode->dsqlContext = context;
procNode->inputSources = context->ctx_proc_inputs;
procNode->dsqlInputArgNames = nodeAs<ProcedureSourceNode>(input)->dsqlInputArgNames;
return procNode;
}
//// TODO: LocalTableSourceNode

View File

@ -467,4 +467,29 @@
#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

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, 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, 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, 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")
@ -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, 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, 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, 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")
@ -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, 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, 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_cond_not_found = 335545288;
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_invalid_sw = 335740930;
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,
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();
jrd_prc* procedure = nullptr;
AutoPtr<string> aliasString;
auto& pool = *tdbb->getDefaultPool();
auto& blrReader = csb->csb_blr_reader;
const auto blrStartPos = blrReader.getPos();
const UCHAR* inArgNamesPos = nullptr;
ObjectsArray<MetaName>* inArgNames = nullptr;
USHORT inArgCount = 0;
QualifiedName name;
const auto node = FB_NEW_POOL(pool) ProcedureSourceNode(pool);
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_pid2:
{
const SSHORT pid = csb->csb_blr_reader.getWord();
const SSHORT pid = blrReader.getWord();
if (blrOp == blr_pid2)
{
aliasString = FB_NEW_POOL(csb->csb_pool) string(csb->csb_pool);
csb->csb_blr_reader.getString(*aliasString);
}
blrReader.getString(node->alias);
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);
break;
@ -928,31 +1045,23 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
case blr_procedure4:
case blr_subproc:
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)
{
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();
}
blrReader.getString(node->alias);
if (blrOp == blr_subproc)
{
DeclareSubProcNode* declareNode;
for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb)
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
{
if (curCsb->subProcedures.get(name.identifier, declareNode))
procedure = declareNode->routine;
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
node->procedure = (*declareNode)->routine;
}
}
else
procedure = MET_lookup_procedure(tdbb, name, false);
node->procedure = MET_lookup_procedure(tdbb, name, false);
break;
@ -960,60 +1069,112 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch
fb_assert(false);
}
if (!procedure)
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
else
if (!node->procedure)
{
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) << Arg::Str(name.toString()) <<
Arg::Warning(isc_modnotfound));
PAR_warning(
Arg::Warning(isc_prcnotdef) << 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);
PAR_error(csb,
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
Arg::Gds(isc_modnotfound));
for (const auto& argName : *inArgNames)
{
CompilerScratch::Dependency dependency(obj_procedure);
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())
PAR_warning(Arg::Warning(isc_illegal_prc_type) << Arg::Str(name));
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, "");
delete node->inputTargets.getObject();
node->inputTargets = nullptr;
}
return node;
@ -1023,7 +1184,7 @@ string ProcedureSourceNode::internalPrint(NodePrinter& printer) const
{
RecordSourceNode::internalPrint(printer);
NODE_PRINT(printer, in_msg);
NODE_PRINT(printer, inputMessage);
NODE_PRINT(printer, context);
return "ProcedureSourceNode";
@ -1040,7 +1201,7 @@ bool ProcedureSourceNode::dsqlAggregateFinder(AggregateFinder& visitor)
if (dsqlContext->ctx_procedure)
{
// Check if an aggregate is buried inside the input parameters.
return visitor.visit(dsqlContext->ctx_proc_inputs);
return visitor.visit(inputSources);
}
return false;
@ -1049,7 +1210,7 @@ bool ProcedureSourceNode::dsqlAggregateFinder(AggregateFinder& visitor)
bool ProcedureSourceNode::dsqlAggregate2Finder(Aggregate2Finder& visitor)
{
if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs);
return visitor.visit(inputSources);
return false;
}
@ -1058,7 +1219,7 @@ bool ProcedureSourceNode::dsqlInvalidReferenceFinder(InvalidReferenceFinder& vis
{
// If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs);
return visitor.visit(inputSources);
return false;
}
@ -1067,7 +1228,7 @@ bool ProcedureSourceNode::dsqlSubSelectFinder(SubSelectFinder& visitor)
{
// If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs);
return visitor.visit(inputSources);
return false;
}
@ -1076,7 +1237,7 @@ bool ProcedureSourceNode::dsqlFieldFinder(FieldFinder& visitor)
{
// If relation is a procedure, check if the parameters are valid.
if (dsqlContext->ctx_procedure)
return visitor.visit(dsqlContext->ctx_proc_inputs);
return visitor.visit(inputSources);
return false;
}
@ -1085,7 +1246,7 @@ RecordSourceNode* ProcedureSourceNode::dsqlFieldRemapper(FieldRemapper& visitor)
{
// Check if relation is a procedure.
if (dsqlContext->ctx_procedure)
doDsqlFieldRemapper(visitor, dsqlContext->ctx_proc_inputs); // Remap the input parameters.
doDsqlFieldRemapper(visitor, inputSources); // Remap the input parameters.
return this;
}
@ -1100,12 +1261,67 @@ bool ProcedureSourceNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const Expr
// Generate blr for a procedure reference.
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->appendMetaString(procedure->prc_name.identifier.c_str());
dsqlScratch->appendMetaString(dsqlProcedure->prc_name.identifier.c_str());
dsqlScratch->appendMetaString(dsqlContext->ctx_alias.c_str());
}
else
@ -1115,20 +1331,20 @@ void ProcedureSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
if (DDL_ids(dsqlScratch))
{
dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_pid2 : blr_pid);
dsqlScratch->appendUShort(procedure->prc_id);
dsqlScratch->appendUShort(dsqlProcedure->prc_id);
}
else
{
if (procedure->prc_name.package.hasData())
if (dsqlProcedure->prc_name.package.hasData())
{
dsqlScratch->appendUChar(dsqlContext->ctx_alias.hasData() ? blr_procedure4 : blr_procedure3);
dsqlScratch->appendMetaString(procedure->prc_name.package.c_str());
dsqlScratch->appendMetaString(procedure->prc_name.identifier.c_str());
dsqlScratch->appendMetaString(dsqlProcedure->prc_name.package.c_str());
dsqlScratch->appendMetaString(dsqlProcedure->prc_name.identifier.c_str());
}
else
{
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);
ValueListNode* inputs = dsqlContext->ctx_proc_inputs;
if (inputs && !(dsqlFlags & DFLAG_PLAN_ITEM))
if (inputSources && !(dsqlFlags & DFLAG_PLAN_ITEM))
{
dsqlScratch->appendUShort(inputs->items.getCount());
dsqlScratch->appendUShort(inputSources->items.getCount());
for (NestConst<ValueExprNode>* ptr = inputs->items.begin();
ptr != inputs->items.end();
++ptr)
{
GEN_expr(dsqlScratch, *ptr);
}
for (auto& arg : inputSources->items)
GEN_expr(dsqlScratch, arg);
}
else
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).
// We must copy the message first and only then use the new pointer to
// copy the inputs properly.
newSource->in_msg = copier.copy(tdbb, in_msg);
newSource->inputMessage = copier.copy(tdbb, inputMessage);
{ // scope
AutoSetRestore<MessageNode*> autoMessage(&copier.message, newSource->in_msg);
newSource->sourceList = copier.copy(tdbb, sourceList);
newSource->targetList = copier.copy(tdbb, targetList);
AutoSetRestore<MessageNode*> autoMessage(&copier.message, newSource->inputMessage);
newSource->inputSources = copier.copy(tdbb, inputSources);
newSource->inputTargets = copier.copy(tdbb, inputTargets);
}
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)
{
doPass1(tdbb, csb, sourceList.getAddress());
doPass1(tdbb, csb, targetList.getAddress());
doPass1(tdbb, csb, in_msg.getAddress());
doPass1(tdbb, csb, inputSources.getAddress());
doPass1(tdbb, csb, inputTargets.getAddress());
doPass1(tdbb, csb, inputMessage.getAddress());
return this;
}
@ -1256,9 +1466,9 @@ void ProcedureSourceNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, Rse
RecordSourceNode* ProcedureSourceNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
ExprNode::doPass2(tdbb, csb, sourceList.getAddress());
ExprNode::doPass2(tdbb, csb, targetList.getAddress());
ExprNode::doPass2(tdbb, csb, in_msg.getAddress());
ExprNode::doPass2(tdbb, csb, inputSources.getAddress());
ExprNode::doPass2(tdbb, csb, inputTargets.getAddress());
ExprNode::doPass2(tdbb, csb, inputMessage.getAddress());
return this;
}
@ -1275,16 +1485,16 @@ RecordSource* ProcedureSourceNode::compile(thread_db* tdbb, Optimizer* opt, bool
const string alias = opt->makeAlias(stream);
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 allowOnlyCurrentStream, ValueExprNode* /*value*/)
{
if (sourceList && !sourceList->computable(csb, stream, allowOnlyCurrentStream))
if (inputSources && !inputSources->computable(csb, stream, allowOnlyCurrentStream))
return false;
if (targetList && !targetList->computable(csb, stream, allowOnlyCurrentStream))
if (inputTargets && !inputTargets->computable(csb, stream, allowOnlyCurrentStream))
return false;
return true;
@ -1293,22 +1503,22 @@ bool ProcedureSourceNode::computable(CompilerScratch* csb, StreamType stream,
void ProcedureSourceNode::findDependentFromStreams(const CompilerScratch* csb,
StreamType currentStream, SortedStreamList* streamList)
{
if (sourceList)
sourceList->findDependentFromStreams(csb, currentStream, streamList);
if (inputSources)
inputSources->findDependentFromStreams(csb, currentStream, streamList);
if (targetList)
targetList->findDependentFromStreams(csb, currentStream, streamList);
if (inputTargets)
inputTargets->findDependentFromStreams(csb, currentStream, streamList);
}
void ProcedureSourceNode::collectStreams(SortedStreamList& streamList) const
{
RecordSourceNode::collectStreams(streamList);
if (sourceList)
sourceList->collectStreams(streamList);
if (inputSources)
inputSources->collectStreams(streamList);
if (targetList)
targetList->collectStreams(streamList);
if (inputTargets)
inputTargets->collectStreams(streamList);
}
@ -1706,7 +1916,7 @@ UnionSourceNode* UnionSourceNode::parse(thread_db* tdbb, CompilerScratch* csb, c
if (node->recursive)
{
stream2 = PAR_context(csb, 0);
stream2 = PAR_context(csb, nullptr);
node->mapStream = stream2;
}
@ -3452,13 +3662,13 @@ static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, Recor
MetaName relName;
string relAlias;
if (auto procNode = nodeAs<ProcedureSourceNode>(source))
if (const auto procNode = nodeAs<ProcedureSourceNode>(source))
{
relName = procNode->dsqlName.identifier;
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;
relAlias = relNode->alias;

View File

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

View File

@ -255,5 +255,8 @@ static const struct
{"outer_map", outer_map},
{NULL, NULL}, // blr_json_function
{"skip_locked", zero},
{"invoke_function", invoke_function},
{"invoke_procedure", invsel_procedure},
{"select_procedure", invsel_procedure},
{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)
{
/**************************************

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::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,
Jrd::SecurityClass::flags_t, ObjectType obj_type, const Jrd::MetaName&,
const Jrd::MetaName& = "");

View File

@ -859,7 +859,7 @@ namespace
return true;
case 6:
Self::checkOutParamDependencies(tdbb, work, transaction);
Self::checkParamDependencies(tdbb, work, transaction);
break;
}
@ -1076,7 +1076,7 @@ namespace
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
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,
@ -1096,7 +1096,7 @@ namespace
static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile);
static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work,
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.
@ -1141,9 +1141,48 @@ namespace
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,
@ -1186,10 +1225,10 @@ namespace
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();
AutoCacheRequest handle(tdbb, irq_out_proc_param_dep, IRQ_REQUESTS);
AutoCacheRequest handle(tdbb, irq_proc_param_dep, IRQ_REQUESTS);
ObjectsArray<string> names;
int depCount = 0;
@ -1202,8 +1241,7 @@ namespace
NOT ANY PP IN RDB$PROCEDURE_PARAMETERS
WITH PP.RDB$PROCEDURE_NAME EQ DEP.RDB$DEPENDED_ON_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_TYPE EQ 1
PP.RDB$PARAMETER_NAME EQ DEP.RDB$FIELD_NAME
{
// 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_linger, // get database linger 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_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)
{
/**************************************
*
* 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);
const auto tail = CMP_csb_element(csb, context);
if (tail->csb_flags & csb_used)
{
@ -867,6 +850,28 @@ StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
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,
const MetaName& field_name)
@ -975,12 +980,12 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
case blr_rid:
case blr_relation2:
case blr_rid2:
{
const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false);
plan->recordSourceNode = relationNode;
relation = relationNode->relation;
}
{
const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false);
plan->recordSourceNode = relationNode;
relation = relationNode->relation;
break;
}
case blr_pid:
case blr_pid2:
@ -989,12 +994,13 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
case blr_procedure3:
case blr_procedure4:
case blr_subproc:
{
const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false);
plan->recordSourceNode = procedureNode;
procedure = procedureNode->procedure;
}
case blr_select_procedure:
{
const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false);
plan->recordSourceNode = procedureNode;
procedure = procedureNode->procedure;
break;
}
case blr_local_table_id:
// TODO
@ -1010,7 +1016,7 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
const StreamType stream = csb->csb_rpt[context].csb_stream;
plan->recordSourceNode->setStream(stream);
// plan->recordSourceNode->context = context; not needed ???
/// plan->recordSourceNode->context = context; not needed ???
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.
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_procedure4:
case blr_subproc:
case blr_select_procedure:
return ProcedureSourceNode::parse(tdbb, csb, blrOp, true);
case blr_rse:
@ -1638,6 +1547,7 @@ DmlNode* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb)
case blr_procedure3:
case blr_procedure4:
case blr_subproc:
case blr_select_procedure:
case blr_relation:
case blr_rid:
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,
ULONG blr_length, Jrd::CompilerScratch*, Jrd::CompilerScratch**, USHORT);
StreamType PAR_context(Jrd::CompilerScratch*, SSHORT*);
StreamType PAR_context2(Jrd::CompilerScratch*, SSHORT*);
void PAR_dependency(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb, StreamType stream,
SSHORT id, const Jrd::MetaName& field_name);
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);
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*);
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 void blr_print_join(gds_ctl*);
static SLONG blr_print_line(gds_ctl*, SSHORT);
static void blr_print_name(gds_ctl*);
static void blr_print_verb(gds_ctl*, SSHORT);
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_dcl_local_table = 31;
const int op_outer_map = 32;
const int op_invoke_function = 33;
const int op_invsel_procedure = 34;
static const UCHAR
// generic print formats
@ -413,7 +416,9 @@ static const UCHAR
erase[] = { op_erase, 0},
local_table[] = { op_word, op_byte, op_literal, op_byte, op_line, 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"
@ -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)
{
/**************************************
@ -3938,6 +3952,206 @@ static void blr_print_verb(gds_ctl* control, SSHORT level)
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:
fb_assert(false);
break;