mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 17:23:03 +01:00
Named arguments for function call, EXECUTE PROCEDURE and procedure record source.
This commit is contained in:
parent
57a629f013
commit
1b2b3ab638
52
doc/sql.extensions/README.named_arguments.md
Normal file
52
doc/sql.extensions/README.named_arguments.md
Normal 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')
|
||||
```
|
@ -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)
|
||||
|
@ -12875,102 +12875,275 @@ 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;
|
||||
|
||||
while ((subCode = blrReader.getByte()) != blr_end)
|
||||
{
|
||||
switch (subCode)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (blrOp == blr_subfunc)
|
||||
{
|
||||
DeclareSubFuncNode* declareNode;
|
||||
blrReader.getMetaName(name.identifier);
|
||||
|
||||
if (functionType == blr_invoke_function_type_sub)
|
||||
{
|
||||
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (curCsb->subFunctions.get(name.identifier, declareNode))
|
||||
if (DeclareSubFuncNode* declareNode; 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;
|
||||
break;
|
||||
}
|
||||
|
||||
if (function)
|
||||
case blr_invoke_function_arg_names:
|
||||
{
|
||||
if (function->isImplemented() && !function->isDefined())
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (!node->function)
|
||||
{
|
||||
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) << Arg::Str(name.toString()) <<
|
||||
PAR_warning(Arg::Warning(isc_funnotdef) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.seekBackward(count);
|
||||
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) <<
|
||||
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
|
||||
{
|
||||
csb->csb_blr_reader.seekBackward(count);
|
||||
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()));
|
||||
}
|
||||
|
||||
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 (argCount > node->function->fun_inputs)
|
||||
mismatchStatus << Arg::Gds(isc_wronumarg);
|
||||
else if (argCount < node->function->fun_inputs - node->function->getDefaultCount())
|
||||
{
|
||||
Parameter* const parameter = function->getInputFields()[i];
|
||||
node->args->items[i] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
|
||||
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())
|
||||
{
|
||||
{ // 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 = function;
|
||||
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,11 +13589,43 @@ 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()));
|
||||
if (node->dsqlArgNames)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (mismatchError)
|
||||
status_exception::raise(mismatchStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned pos = 0;
|
||||
|
||||
for (auto& arg : node->args->items)
|
||||
@ -13386,17 +13633,13 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
if (pos < node->dsqlFunction->udf_arguments.getCount())
|
||||
{
|
||||
PASS1_set_parameter_type(dsqlScratch, arg,
|
||||
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos]; },
|
||||
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos].desc; },
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We should complain here in the future! The parameter is
|
||||
// out of bounds or the function doesn't declare input params.
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,80 +2905,305 @@ 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);
|
||||
switch (blrOp)
|
||||
{
|
||||
case blr_invoke_procedure:
|
||||
{
|
||||
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_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");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_exec_pid:
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 (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) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
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);
|
||||
}
|
||||
|
||||
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 (inArgNames)
|
||||
{
|
||||
for (const auto& argName : *inArgNames)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->inputSources && node->inputSources->items.isEmpty())
|
||||
{
|
||||
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,30 +3246,65 @@ 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()));
|
||||
if (node->dsqlInputArgNames)
|
||||
{
|
||||
fb_assert(node->dsqlInputArgNames->getCount() == node->inputSources->items.getCount());
|
||||
|
||||
node->inputSources = doDsqlPass(dsqlScratch, inputSources);
|
||||
LeftPooledMap<MetaName, const dsql_fld*> argsByName;
|
||||
|
||||
if (count)
|
||||
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)
|
||||
{
|
||||
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 desc_node;
|
||||
dsc descNode;
|
||||
|
||||
NestConst<ValueExprNode>* ptr = node->inputSources->items.begin();
|
||||
const NestConst<ValueExprNode>* end = node->inputSources->items.end();
|
||||
auto ptr = node->inputSources->items.begin();
|
||||
const auto end = node->inputSources->items.end();
|
||||
|
||||
for (const dsql_fld* field = procedure->prc_inputs; ptr != end; ++ptr, field = field->fld_next)
|
||||
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(&desc_node, field);
|
||||
DsqlDescMaker::fromField(&descNode, field);
|
||||
PASS1_set_parameter_type(dsqlScratch, *ptr,
|
||||
[&] (dsc* desc) { *desc = desc_node; },
|
||||
[&] (dsc* desc) { *desc = descNode; },
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle output parameters.
|
||||
|
||||
@ -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,15 +3403,21 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
}
|
||||
}
|
||||
|
||||
if (dsqlInputArgNames)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_invoke_procedure);
|
||||
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_type);
|
||||
|
||||
if (dsqlName.package.hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_exec_proc2);
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_type_packaged);
|
||||
dsqlScratch->appendMetaString(dsqlName.package.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
dsqlScratch->appendUChar(
|
||||
(dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc);
|
||||
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ?
|
||||
blr_invsel_procedure_type_sub : blr_invsel_procedure_type_standalone);
|
||||
}
|
||||
|
||||
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
|
||||
@ -3153,9 +3425,52 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
// 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());
|
||||
NestConst<ValueExprNode>* ptr = inputSources->items.begin();
|
||||
const NestConst<ValueExprNode>* end = inputSources->items.end();
|
||||
|
||||
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
|
||||
{
|
||||
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++);
|
||||
@ -3167,13 +3482,14 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
if (outputSources)
|
||||
{
|
||||
dsqlScratch->appendUShort(outputSources->items.getCount());
|
||||
NestConst<ValueExprNode>* ptr = outputSources->items.begin();
|
||||
auto ptr = outputSources->items.begin();
|
||||
|
||||
for (const NestConst<ValueExprNode>* end = outputSources->items.end(); ptr != end; ++ptr)
|
||||
for (const auto end = outputSources->items.end(); ptr != end; ++ptr)
|
||||
GEN_expr(dsqlScratch, *ptr);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
}
|
||||
|
||||
if (message)
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
src/dsql/parse.y
110
src/dsql/parse.y
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
{
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
blrReader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (procedure->prc_type == prc_executable)
|
||||
{
|
||||
const string name = procedure->getName().toString();
|
||||
|
||||
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)
|
||||
{
|
||||
if (blrOp != blr_select_procedure)
|
||||
{
|
||||
node->stream = PAR_context(csb, &node->context);
|
||||
|
||||
csb->csb_rpt[node->stream].csb_procedure = procedure;
|
||||
csb->csb_rpt[node->stream].csb_alias = aliasString.release();
|
||||
csb->csb_rpt[node->stream].csb_procedure = node->procedure;
|
||||
csb->csb_rpt[node->stream].csb_alias = &node->alias;
|
||||
|
||||
PAR_procedure_parms(tdbb, csb, procedure, node->in_msg.getAddress(),
|
||||
node->sourceList.getAddress(), node->targetList.getAddress(), true);
|
||||
inArgCount = blrReader.getWord();
|
||||
node->inputSources = PAR_args(tdbb, csb, inArgCount, inArgCount);
|
||||
}
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
PAR_dependency(tdbb, csb, node->stream, (SSHORT) -1, "");
|
||||
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);
|
||||
}
|
||||
|
||||
if (inArgNames)
|
||||
{
|
||||
for (const auto& argName : *inArgNames)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = node->procedure;
|
||||
dependency.subName = &argName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->inputSources && node->inputSources->items.isEmpty())
|
||||
{
|
||||
delete node->inputSources.getObject();
|
||||
node->inputSources = nullptr;
|
||||
|
||||
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;
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
};
|
||||
|
167
src/jrd/cmp.cpp
167
src/jrd/cmp.cpp
@ -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)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -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& = "");
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
154
src/jrd/par.cpp
154
src/jrd/par.cpp
@ -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)
|
||||
@ -979,8 +984,8 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
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:
|
||||
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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user