mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 10:00:38 +01:00
Merge pull request #7557 from FirebirdSQL/work/named-arguments
Named arguments, DEFAULT in argument list and CALL statement
This commit is contained in:
commit
ffd31e3303
103
doc/sql.extensions/README.call.md
Normal file
103
doc/sql.extensions/README.call.md
Normal file
@ -0,0 +1,103 @@
|
||||
# CALL statement (FB 6.0)
|
||||
|
||||
`CALL` statement is similar to `EXECUTE PROCEDURE`, but allow the caller to get specific output parameters, or none.
|
||||
|
||||
When using the positional or mixed parameter passing, output parameters follows the input ones.
|
||||
|
||||
When passing `NULL` to output parameters, they are ignored, and in the case of DSQL, not even returned.
|
||||
|
||||
In DSQL output parameters are specified using `?`, and in PSQL using the target variables or parameters.
|
||||
|
||||
## Syntax
|
||||
|
||||
```
|
||||
<call statement> ::=
|
||||
CALL [<package name> .] <procedure name> (<arguments>)
|
||||
|
||||
<arguments> ::=
|
||||
<positional arguments> |
|
||||
[ {<positional arguments>,} ] <named arguments>
|
||||
|
||||
<positional arguments> ::=
|
||||
<value or default> [ {, <value or default>}... ]
|
||||
|
||||
<named arguments> ::=
|
||||
<named argument> [ {, <named argument>}... ]
|
||||
|
||||
<named argument> ::=
|
||||
<argument name> => <value or default>
|
||||
|
||||
<value or default> ::=
|
||||
<value> |
|
||||
DEFAULT
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
create or alter procedure insert_customer (
|
||||
last_name varchar(30),
|
||||
first_name varchar(30)
|
||||
) returns (
|
||||
id integer,
|
||||
full_name varchar(62)
|
||||
)
|
||||
as
|
||||
begin
|
||||
insert into customers (last_name, first_name)
|
||||
values (:last_name, :first_name)
|
||||
returning id, last_name || ', ' || first_name
|
||||
into :id, :full_name;
|
||||
end
|
||||
```
|
||||
|
||||
```
|
||||
-- Not all output parameters are necessary.
|
||||
call insert_customer(
|
||||
'LECLERC',
|
||||
'CHARLES',
|
||||
?)
|
||||
```
|
||||
|
||||
```
|
||||
-- Ignore first output parameter (using NULL) and get the second.
|
||||
call insert_customer(
|
||||
'LECLERC',
|
||||
'CHARLES',
|
||||
null,
|
||||
?)
|
||||
```
|
||||
|
||||
```
|
||||
-- Ignore ID output parameter.
|
||||
call insert_customer(
|
||||
'LECLERC',
|
||||
'CHARLES',
|
||||
full_name => ?)
|
||||
```
|
||||
|
||||
```
|
||||
-- Pass inputs and get outputs using named arguments.
|
||||
call insert_customer(
|
||||
last_name => 'LECLERC',
|
||||
first_name => 'CHARLES',
|
||||
last_name => ?,
|
||||
id => ?)
|
||||
```
|
||||
|
||||
```
|
||||
create or alter procedure do_something_and_insert_customer returns (
|
||||
out_id integer,
|
||||
out_full_name varchar(62)
|
||||
)
|
||||
as
|
||||
declare last_name varchar(30);
|
||||
declare first_name varchar(30);
|
||||
begin
|
||||
call insert_customer(
|
||||
last_name,
|
||||
first_name,
|
||||
out_id,
|
||||
full_name => out_full_name);
|
||||
end
|
||||
```
|
73
doc/sql.extensions/README.named_arguments.md
Normal file
73
doc/sql.extensions/README.named_arguments.md
Normal file
@ -0,0 +1,73 @@
|
||||
# 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 those who have default values.
|
||||
|
||||
As the positional syntax, all arguments without default values are required to be present in the call.
|
||||
|
||||
A call can use positional, named or mixed arguments. In mixed syntax, positional arguments must appear before
|
||||
named arguments.
|
||||
|
||||
## 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> ::=
|
||||
<positional arguments> |
|
||||
[ {<positional arguments>,} ] <named arguments>
|
||||
|
||||
<positional arguments> ::=
|
||||
<value or default> [ {, <value or default>}... ]
|
||||
|
||||
<named arguments> ::=
|
||||
<named argument> [ {, <named argument>}... ]
|
||||
|
||||
<named argument> ::=
|
||||
<argument name> => <value or default>
|
||||
|
||||
|
||||
<value or default> ::=
|
||||
<value> |
|
||||
DEFAULT
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
select function_name(parameter2 => 'Two', parameter1 => 1)
|
||||
from rdb$database
|
||||
```
|
||||
|
||||
```
|
||||
select function_name(1, parameter2 => 'Two')
|
||||
from rdb$database
|
||||
```
|
||||
|
||||
```
|
||||
select function_name(default, parameter2 => 'Two')
|
||||
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)
|
||||
@ -103,6 +104,7 @@ PARSER_TOKEN(TOK_BOOLEAN, "BOOLEAN", false)
|
||||
PARSER_TOKEN(TOK_BOTH, "BOTH", false)
|
||||
PARSER_TOKEN(TOK_BREAK, "BREAK", true)
|
||||
PARSER_TOKEN(TOK_BY, "BY", false)
|
||||
PARSER_TOKEN(TOK_CALL, "CALL", false)
|
||||
PARSER_TOKEN(TOK_CALLER, "CALLER", true)
|
||||
PARSER_TOKEN(TOK_CASCADE, "CASCADE", true)
|
||||
PARSER_TOKEN(TOK_CASE, "CASE", false)
|
||||
|
@ -299,6 +299,10 @@ public:
|
||||
|
||||
size_t count() const { return mCount; }
|
||||
|
||||
bool isEmpty() const { return mCount == 0; }
|
||||
|
||||
bool hasData() const { return mCount != 0; }
|
||||
|
||||
Accessor accessor()
|
||||
{
|
||||
return Accessor(this);
|
||||
|
@ -486,11 +486,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void ensureCapacity(size_type newcapacity)
|
||||
{
|
||||
ensureCapacity(newcapacity, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
size_type count, capacity;
|
||||
T* data;
|
||||
|
||||
void ensureCapacity(size_type newcapacity, bool preserve = true)
|
||||
void ensureCapacity(size_type newcapacity, bool preserve)
|
||||
{
|
||||
if (newcapacity > capacity)
|
||||
{
|
||||
|
@ -12875,102 +12875,265 @@ dsc* TrimNode::execute(thread_db* tdbb, Request* request) const
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<UdfCallNode> regUdfCallNode({blr_function, blr_function2, blr_subfunc});
|
||||
static RegisterNode<UdfCallNode> regUdfCallNode({blr_function, blr_function2, blr_subfunc, blr_invoke_function});
|
||||
|
||||
UdfCallNode::UdfCallNode(MemoryPool& pool, const QualifiedName& aName, ValueListNode* aArgs)
|
||||
UdfCallNode::UdfCallNode(MemoryPool& pool, const QualifiedName& aName,
|
||||
ValueListNode* aArgs, ObjectsArray<MetaName>* aDsqlArgNames)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_UDF_CALL>(pool),
|
||||
name(pool, aName),
|
||||
args(aArgs),
|
||||
function(NULL),
|
||||
dsqlFunction(NULL),
|
||||
isSubRoutine(false)
|
||||
dsqlArgNames(aDsqlArgNames)
|
||||
{
|
||||
}
|
||||
|
||||
DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
||||
const UCHAR blrOp)
|
||||
{
|
||||
const UCHAR* savePos = csb->csb_blr_reader.getPos();
|
||||
const auto predateCheck = [&](bool condition, const char* preVerb, const char* postVerb)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
string str;
|
||||
str.printf("%s should predate %s", preVerb, postVerb);
|
||||
PAR_error(csb, Arg::Gds(isc_random) << str);
|
||||
}
|
||||
};
|
||||
|
||||
auto& blrReader = csb->csb_blr_reader;
|
||||
const UCHAR* startPos = csb->csb_blr_reader.getPos();
|
||||
|
||||
const UCHAR* argNamesPos = nullptr;
|
||||
ObjectsArray<MetaName>* argNames = nullptr;
|
||||
USHORT argCount = 0;
|
||||
QualifiedName name;
|
||||
|
||||
if (blrOp == blr_function2)
|
||||
csb->csb_blr_reader.getMetaName(name.package);
|
||||
const auto node = FB_NEW_POOL(pool) UdfCallNode(pool);
|
||||
|
||||
csb->csb_blr_reader.getMetaName(name.identifier);
|
||||
|
||||
const USHORT count = name.package.length() + name.identifier.length();
|
||||
|
||||
UdfCallNode* node = FB_NEW_POOL(pool) UdfCallNode(pool, name);
|
||||
|
||||
if (blrOp == blr_function &&
|
||||
(name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT"))
|
||||
if (blrOp == blr_invoke_function)
|
||||
{
|
||||
csb->csb_blr_reader.setPos(savePos);
|
||||
return SysFuncCallNode::parse(tdbb, pool, csb, blr_sys_function);
|
||||
}
|
||||
UCHAR subCode;
|
||||
|
||||
if (blrOp == blr_subfunc)
|
||||
{
|
||||
DeclareSubFuncNode* declareNode;
|
||||
|
||||
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
|
||||
while ((subCode = blrReader.getByte()) != blr_end)
|
||||
{
|
||||
if (curCsb->subFunctions.get(name.identifier, declareNode))
|
||||
node->function = declareNode->routine;
|
||||
}
|
||||
}
|
||||
|
||||
Function* function = node->function;
|
||||
|
||||
if (!function)
|
||||
function = node->function = Function::lookup(tdbb, name, false);
|
||||
|
||||
if (function)
|
||||
{
|
||||
if (function->isImplemented() && !function->isDefined())
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
switch (subCode)
|
||||
{
|
||||
PAR_warning(Arg::Warning(isc_funnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.seekBackward(count);
|
||||
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
case blr_invoke_function_type:
|
||||
{
|
||||
UCHAR functionType = blrReader.getByte();
|
||||
|
||||
switch (functionType)
|
||||
{
|
||||
case blr_invoke_function_type_packaged:
|
||||
blrReader.getMetaName(name.package);
|
||||
break;
|
||||
|
||||
case blr_invoke_function_type_standalone:
|
||||
case blr_invoke_function_type_sub:
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_function_type");
|
||||
break;
|
||||
}
|
||||
|
||||
blrReader.getMetaName(name.identifier);
|
||||
|
||||
if (functionType == blr_invoke_function_type_sub)
|
||||
{
|
||||
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (DeclareSubFuncNode* declareNode; curCsb->subFunctions.get(name.identifier, declareNode))
|
||||
node->function = declareNode->routine;
|
||||
}
|
||||
}
|
||||
else if (!node->function)
|
||||
node->function = Function::lookup(tdbb, name, false);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_invoke_function_arg_names:
|
||||
{
|
||||
predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_arg_names");
|
||||
predateCheck(!node->args, "blr_invoke_function_arg_names", "blr_invoke_function_arg_names");
|
||||
|
||||
argNamesPos = blrReader.getPos();
|
||||
USHORT argNamesCount = blrReader.getWord();
|
||||
MetaName argName;
|
||||
|
||||
argNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
|
||||
while (argNamesCount--)
|
||||
{
|
||||
blrReader.getMetaName(argName);
|
||||
argNames->add(argName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_invoke_function_args:
|
||||
predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_args");
|
||||
|
||||
argCount = blrReader.getWord();
|
||||
node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function->fun_inputs));
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_function sub code");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.seekBackward(count);
|
||||
PAR_error(csb, Arg::Gds(isc_funnotdef) << Arg::Str(name.toString()));
|
||||
if (blrOp == blr_function2)
|
||||
blrReader.getMetaName(name.package);
|
||||
|
||||
blrReader.getMetaName(name.identifier);
|
||||
|
||||
if (blrOp == blr_function &&
|
||||
(name.identifier == "RDB$GET_CONTEXT" || name.identifier == "RDB$SET_CONTEXT"))
|
||||
{
|
||||
blrReader.setPos(startPos);
|
||||
return SysFuncCallNode::parse(tdbb, pool, csb, blr_sys_function);
|
||||
}
|
||||
|
||||
if (blrOp == blr_subfunc)
|
||||
{
|
||||
for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (DeclareSubFuncNode* declareNode; curCsb->subFunctions.get(name.identifier, declareNode))
|
||||
node->function = declareNode->routine;
|
||||
}
|
||||
}
|
||||
else if (!node->function)
|
||||
node->function = Function::lookup(tdbb, name, false);
|
||||
|
||||
argCount = blrReader.getByte();
|
||||
node->args = PAR_args(tdbb, csb, argCount, node->function->fun_inputs);
|
||||
}
|
||||
|
||||
node->isSubRoutine = function->isSubRoutine();
|
||||
|
||||
const UCHAR argCount = csb->csb_blr_reader.getByte();
|
||||
|
||||
// Check to see if the argument count matches.
|
||||
if (argCount < function->fun_inputs - function->getDefaultCount() || argCount > function->fun_inputs)
|
||||
PAR_error(csb, Arg::Gds(isc_funmismat) << name.toString());
|
||||
|
||||
node->args = PAR_args(tdbb, csb, argCount, function->fun_inputs);
|
||||
|
||||
for (USHORT i = argCount; i < function->fun_inputs; ++i)
|
||||
if (argNames && argNames->getCount() > argCount)
|
||||
{
|
||||
Parameter* const parameter = function->getInputFields()[i];
|
||||
node->args->items[i] = CMP_clone_node(tdbb, csb, parameter->prm_default_value);
|
||||
blrReader.setPos(argNamesPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_random) <<
|
||||
"blr_invoke_function_arg_names count cannot be greater than blr_invoke_function_args");
|
||||
}
|
||||
|
||||
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) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
blrReader.setPos(startPos);
|
||||
PAR_error(csb, Arg::Gds(isc_funnotdef) << name.toString() <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
}
|
||||
|
||||
node->name = name;
|
||||
node->isSubRoutine = node->function->isSubRoutine();
|
||||
|
||||
Arg::StatusVector mismatchStatus;
|
||||
|
||||
if (!node->args)
|
||||
node->args = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
|
||||
const auto positionalArgCount = argNames ? argCount - argNames->getCount() : argCount;
|
||||
auto argIt = node->args->items.begin();
|
||||
LeftPooledMap<MetaName, NestConst<ValueExprNode>> argsByName;
|
||||
|
||||
if (positionalArgCount)
|
||||
{
|
||||
if (argCount > node->function->fun_inputs)
|
||||
mismatchStatus << Arg::Gds(isc_wronumarg);
|
||||
|
||||
for (auto pos = 0; pos < positionalArgCount; ++pos)
|
||||
{
|
||||
if (pos < node->function->fun_inputs)
|
||||
{
|
||||
const auto& parameter = node->function->getInputFields()[pos];
|
||||
|
||||
if (argsByName.put(parameter->prm_name, *argIt))
|
||||
mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << parameter->prm_name;
|
||||
}
|
||||
|
||||
++argIt;
|
||||
}
|
||||
}
|
||||
|
||||
if (argNames)
|
||||
{
|
||||
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())
|
||||
{
|
||||
const auto argValue = argsByName.get(parameter->prm_name);
|
||||
|
||||
if (argValue)
|
||||
{
|
||||
*argIt = *argValue;
|
||||
argsByName.remove(parameter->prm_name);
|
||||
}
|
||||
|
||||
if (!argValue || !*argValue)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_fun_param_mismatch) << name.toString() << mismatchStatus);
|
||||
|
||||
// CVC: I will track ufds only if a function is not being dropped.
|
||||
if (!function->isSubRoutine() && csb->collectingDependencies())
|
||||
if (!node->function->isSubRoutine() && csb->collectingDependencies())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_udf);
|
||||
dependency.function = function;
|
||||
csb->addDependency(dependency);
|
||||
{ // scope
|
||||
CompilerScratch::Dependency dependency(obj_udf);
|
||||
dependency.function = node->function;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
|
||||
if (argNames)
|
||||
{
|
||||
for (const auto& argName : *argNames)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_udf);
|
||||
dependency.function = node->function;
|
||||
dependency.subName = &argName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
@ -12993,6 +13156,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_arg(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
|
||||
@ -13005,7 +13207,7 @@ void UdfCallNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
dsqlScratch->appendUChar(args->items.getCount());
|
||||
|
||||
for (auto& arg : args->items)
|
||||
GEN_expr(dsqlScratch, arg);
|
||||
GEN_arg(dsqlScratch, arg);
|
||||
}
|
||||
|
||||
void UdfCallNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc)
|
||||
@ -13356,8 +13558,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())
|
||||
{
|
||||
@ -13375,30 +13580,50 @@ 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()));
|
||||
|
||||
auto argIt = node->args->items.begin();
|
||||
unsigned pos = 0;
|
||||
|
||||
for (auto& arg : node->args->items)
|
||||
while (pos < node->args->items.getCount() - (node->dsqlArgNames ? node->dsqlArgNames->getCount() : 0))
|
||||
{
|
||||
if (pos < node->dsqlFunction->udf_arguments.getCount())
|
||||
{
|
||||
PASS1_set_parameter_type(dsqlScratch, arg,
|
||||
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos]; },
|
||||
PASS1_set_parameter_type(dsqlScratch, *argIt++,
|
||||
[&] (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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Arg::StatusVector mismatchStatus;
|
||||
|
||||
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
|
||||
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
|
||||
|
||||
++argIt;
|
||||
}
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_fun_param_mismatch) << name.toString() << mismatchStatus);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1290,6 +1296,11 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
void ensureCapacity(unsigned count)
|
||||
{
|
||||
items.ensureCapacity(count);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
items.clear();
|
||||
|
@ -1664,8 +1664,8 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
|
||||
|
||||
if (!implemetingForward)
|
||||
{
|
||||
// ASF: dsqlFunction->udf_arguments is only checked for its count for now.
|
||||
dsqlFunction->udf_arguments.add(dsc());
|
||||
// ASF: dsqlFunction->udf_arguments types (desc) are not checked for now.
|
||||
dsqlFunction->udf_arguments.add().name = param->name;
|
||||
}
|
||||
|
||||
if (param->defaultClause)
|
||||
@ -2905,79 +2905,401 @@ 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;
|
||||
const UCHAR* inOutArgNamesPos = nullptr;
|
||||
ObjectsArray<MetaName>* inOutArgNames = nullptr;
|
||||
USHORT inOutArgCount = 0;
|
||||
ValueListNode* inOutArgs = nullptr;
|
||||
QualifiedName name;
|
||||
|
||||
if (blrOp == blr_exec_pid)
|
||||
{
|
||||
const USHORT pid = csb->csb_blr_reader.getWord();
|
||||
if (!(procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
|
||||
name.identifier.printf("id %d", pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blrOp == blr_exec_proc2)
|
||||
csb->csb_blr_reader.getMetaName(name.package);
|
||||
const auto node = FB_NEW_POOL(pool) ExecProcedureNode(pool);
|
||||
|
||||
csb->csb_blr_reader.getMetaName(name.identifier);
|
||||
|
||||
if (blrOp == blr_exec_subproc)
|
||||
switch (blrOp)
|
||||
{
|
||||
case blr_invoke_procedure:
|
||||
{
|
||||
DeclareSubProcNode* declareNode;
|
||||
UCHAR subCode;
|
||||
|
||||
for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb)
|
||||
while ((subCode = blrReader.getByte()) != blr_end)
|
||||
{
|
||||
if (curCsb->subProcedures.get(name.identifier, declareNode))
|
||||
procedure = declareNode->routine;
|
||||
switch (subCode)
|
||||
{
|
||||
case blr_invsel_procedure_type:
|
||||
{
|
||||
UCHAR procedureType = blrReader.getByte();
|
||||
|
||||
switch (procedureType)
|
||||
{
|
||||
case blr_invsel_procedure_type_packaged:
|
||||
blrReader.getMetaName(name.package);
|
||||
break;
|
||||
|
||||
case blr_invsel_procedure_type_standalone:
|
||||
case blr_invsel_procedure_type_sub:
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invsel_procedure_type");
|
||||
break;
|
||||
}
|
||||
|
||||
blrReader.getMetaName(name.identifier);
|
||||
|
||||
if (procedureType == blr_invsel_procedure_type_sub)
|
||||
{
|
||||
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
|
||||
node->procedure = (*declareNode)->routine;
|
||||
}
|
||||
}
|
||||
else if (!node->procedure)
|
||||
node->procedure = MET_lookup_procedure(tdbb, name, false);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_invsel_procedure_in_arg_names:
|
||||
{
|
||||
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_arg_names");
|
||||
predateCheck(!node->inputSources,
|
||||
"blr_invsel_procedure_in_arg_names", "blr_invsel_procedure_in_args");
|
||||
|
||||
inArgNamesPos = blrReader.getPos();
|
||||
USHORT inArgNamesCount = blrReader.getWord();
|
||||
MetaName argName;
|
||||
|
||||
inArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
|
||||
while (inArgNamesCount--)
|
||||
{
|
||||
blrReader.getMetaName(argName);
|
||||
inArgNames->add(argName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_invsel_procedure_in_args:
|
||||
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_args");
|
||||
inArgCount = blrReader.getWord();
|
||||
node->inputSources = PAR_args(tdbb, csb, inArgCount,
|
||||
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;
|
||||
|
||||
case blr_invsel_procedure_inout_arg_names:
|
||||
{
|
||||
predateCheck(node->procedure,
|
||||
"blr_invsel_procedure_type", "blr_invsel_procedure_inout_arg_names");
|
||||
predateCheck(!inOutArgs,
|
||||
"blr_invsel_procedure_inout_arg_names", "blr_invsel_procedure_inout_args");
|
||||
|
||||
inOutArgNamesPos = blrReader.getPos();
|
||||
USHORT inOutArgNamesCount = blrReader.getWord();
|
||||
MetaName argName;
|
||||
|
||||
inOutArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
|
||||
while (inOutArgNamesCount--)
|
||||
{
|
||||
blrReader.getMetaName(argName);
|
||||
inOutArgNames->add(argName);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_invsel_procedure_inout_args:
|
||||
predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_inout_args");
|
||||
inOutArgCount = blrReader.getWord();
|
||||
inOutArgs = PAR_args(tdbb, csb, inOutArgCount, inOutArgCount);
|
||||
break;
|
||||
|
||||
default:
|
||||
PAR_error(csb, Arg::Gds(isc_random) << "Invalid blr_invoke_procedure sub code");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
procedure = MET_lookup_procedure(tdbb, name, false);
|
||||
}
|
||||
|
||||
if (!procedure)
|
||||
PAR_error(csb, Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()));
|
||||
else
|
||||
{
|
||||
if (procedure->isImplemented() && !procedure->isDefined())
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_exec_pid:
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
const USHORT pid = blrReader.getWord();
|
||||
if (!(node->procedure = MET_lookup_procedure_id(tdbb, pid, false, false, 0)))
|
||||
name.identifier.printf("id %d", pid);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (blrOp == blr_exec_proc2)
|
||||
blrReader.getMetaName(name.package);
|
||||
|
||||
blrReader.getMetaName(name.identifier);
|
||||
|
||||
if (blrOp == blr_exec_subproc)
|
||||
{
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
for (auto curCsb = csb; curCsb && !node->procedure; curCsb = curCsb->mainCsb)
|
||||
{
|
||||
if (const auto declareNode = curCsb->subProcedures.get(name.identifier))
|
||||
node->procedure = (*declareNode)->routine;
|
||||
}
|
||||
}
|
||||
else
|
||||
node->procedure = MET_lookup_procedure(tdbb, name, false);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!node->procedure)
|
||||
{
|
||||
blrReader.setPos(blrStartPos);
|
||||
PAR_error(csb, Arg::Gds(isc_prcnotdef) << name.toString());
|
||||
}
|
||||
|
||||
if ((inOutArgs || inOutArgNames) && (node->inputSources || inArgNames || node->outputTargets || outArgNames))
|
||||
{
|
||||
blrReader.setPos(inOutArgNamesPos);
|
||||
PAR_error(csb, Arg::Gds(isc_random) << "IN/OUT args are not allowed with IN or OUT args");
|
||||
}
|
||||
|
||||
if (inOutArgs)
|
||||
{
|
||||
node->outputTargets = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
|
||||
const auto prcInCount = node->procedure->getInputFields().getCount();
|
||||
const auto positionalArgCount = inOutArgs->items.getCount() -
|
||||
(inOutArgNames ? inOutArgNames->getCount() : 0);
|
||||
|
||||
if (positionalArgCount > prcInCount || inOutArgNames)
|
||||
{
|
||||
node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
inArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
outArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
SortedObjectsArray<MetaName> outFields;
|
||||
|
||||
for (const auto field : node->procedure->getOutputFields())
|
||||
outFields.add(field->prm_name);
|
||||
|
||||
unsigned pos = 0;
|
||||
|
||||
for (auto source : inOutArgs->items)
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
const bool isInput = (pos < positionalArgCount && pos < prcInCount) ||
|
||||
(pos >= positionalArgCount &&
|
||||
!outFields.exist((*inOutArgNames)[pos - positionalArgCount]));
|
||||
|
||||
if (isInput)
|
||||
{
|
||||
node->inputSources->add(source);
|
||||
|
||||
if (pos >= positionalArgCount)
|
||||
inArgNames->add((*inOutArgNames)[pos - positionalArgCount]);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->outputTargets->add(source);
|
||||
|
||||
if (pos >= positionalArgCount)
|
||||
outArgNames->add((*inOutArgNames)[pos - positionalArgCount]);
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
delete inOutArgs;
|
||||
inOutArgs = nullptr;
|
||||
|
||||
delete inOutArgNames;
|
||||
inOutArgNames = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->inputSources = inOutArgs;
|
||||
inArgNames = inOutArgNames;
|
||||
}
|
||||
|
||||
inArgCount = node->inputSources->items.getCount();
|
||||
outArgCount = node->outputTargets->items.getCount();
|
||||
|
||||
node->inputSources->ensureCapacity(node->procedure->getInputFields().getCount());
|
||||
node->outputTargets->ensureCapacity(node->procedure->getOutputFields().getCount());
|
||||
}
|
||||
|
||||
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 cannot be greater than blr_invsel_procedure_in_args");
|
||||
}
|
||||
|
||||
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 (outArgNames && outArgNames->getCount() > node->outputTargets->items.getCount())
|
||||
{
|
||||
blrReader.setPos(outArgNamesPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_random) <<
|
||||
"blr_invsel_procedure_out_arg_names count cannot be greater than blr_invsel_procedure_out_args");
|
||||
}
|
||||
|
||||
if (node->procedure->isImplemented() && !node->procedure->isDefined())
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
{
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
}
|
||||
|
||||
node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount());
|
||||
|
||||
Arg::StatusVector 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.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_prcmismat) << node->procedure->getName().toString() << 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 (outArgNames)
|
||||
{
|
||||
for (const auto& argName : *outArgNames)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = node->procedure;
|
||||
dependency.subName = &argName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExecProcedureNode* node = FB_NEW_POOL(pool) ExecProcedureNode(pool);
|
||||
node->procedure = procedure;
|
||||
|
||||
PAR_procedure_parms(tdbb, csb, procedure, node->inputMessage.getAddress(),
|
||||
node->inputSources.getAddress(), node->inputTargets.getAddress(), true);
|
||||
PAR_procedure_parms(tdbb, csb, procedure, node->outputMessage.getAddress(),
|
||||
node->outputSources.getAddress(), node->outputTargets.getAddress(), false);
|
||||
|
||||
if (csb->collectingDependencies() && !procedure->isSubRoutine())
|
||||
if (node->inputSources && node->inputSources->items.isEmpty())
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = procedure;
|
||||
csb->addDependency(dependency);
|
||||
delete node->inputSources.getObject();
|
||||
node->inputSources = nullptr;
|
||||
|
||||
delete node->inputTargets.getObject();
|
||||
node->inputTargets = nullptr;
|
||||
}
|
||||
|
||||
if (node->outputSources && node->outputSources->items.isEmpty())
|
||||
{
|
||||
delete node->outputSources.getObject();
|
||||
node->outputSources = nullptr;
|
||||
|
||||
delete node->outputTargets.getObject();
|
||||
node->outputTargets = nullptr;
|
||||
}
|
||||
|
||||
return node;
|
||||
@ -2985,7 +3307,8 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr
|
||||
|
||||
ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsql_prc* procedure = NULL;
|
||||
auto& pool = dsqlScratch->getPool();
|
||||
dsql_prc* procedure = nullptr;
|
||||
|
||||
if (dsqlName.package.isEmpty())
|
||||
{
|
||||
@ -3001,13 +3324,145 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
|
||||
Arg::Gds(isc_dsql_procedure_err) <<
|
||||
Arg::Gds(isc_random) <<
|
||||
Arg::Str(dsqlName.toString()));
|
||||
dsqlName.toString());
|
||||
}
|
||||
|
||||
if (!dsqlScratch->isPsql())
|
||||
dsqlScratch->getDsqlStatement()->setType(DsqlStatement::TYPE_EXEC_PROCEDURE);
|
||||
|
||||
ExecProcedureNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ExecProcedureNode(dsqlScratch->getPool(), dsqlName);
|
||||
if (dsqlCallSyntax && !dsqlScratch->isPsql() && inputSources && inputSources->items.hasData())
|
||||
{
|
||||
const auto positionalArgCount = inputSources->items.getCount() -
|
||||
(dsqlInputArgNames ? dsqlInputArgNames->getCount() : 0);
|
||||
|
||||
if (positionalArgCount > procedure->prc_in_count || dsqlInputArgNames)
|
||||
{
|
||||
const auto newInputs = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
const auto newOutputs = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
const auto newInputArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
const auto newOutputArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool);
|
||||
SortedObjectsArray<MetaName> outFields;
|
||||
|
||||
for (const auto* field = procedure->prc_outputs; field; field = field->fld_next)
|
||||
outFields.add(field->fld_name);
|
||||
|
||||
unsigned pos = 0;
|
||||
|
||||
for (auto source : inputSources->items)
|
||||
{
|
||||
const bool isInput = (pos < positionalArgCount && pos < procedure->prc_in_count) ||
|
||||
(pos >= positionalArgCount &&
|
||||
!outFields.exist((*dsqlInputArgNames)[pos - positionalArgCount]));
|
||||
|
||||
if (isInput)
|
||||
{
|
||||
newInputs->add(source);
|
||||
|
||||
if (pos >= positionalArgCount)
|
||||
newInputArgNames->add((*dsqlInputArgNames)[pos - positionalArgCount]);
|
||||
}
|
||||
else
|
||||
{
|
||||
newOutputs->add(source);
|
||||
|
||||
if (pos >= positionalArgCount)
|
||||
newOutputArgNames->add((*dsqlInputArgNames)[pos - positionalArgCount]);
|
||||
}
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (newInputs->items.getCount() != inputSources->items.getCount())
|
||||
{
|
||||
delete inputSources.getObject();
|
||||
inputSources = newInputs;
|
||||
|
||||
delete dsqlInputArgNames.getObject();
|
||||
dsqlInputArgNames = newInputArgNames;
|
||||
|
||||
delete outputTargets.getObject();
|
||||
outputTargets = newOutputs;
|
||||
|
||||
delete dsqlOutputArgNames.getObject();
|
||||
dsqlOutputArgNames = newOutputArgNames;
|
||||
}
|
||||
|
||||
if (outputTargets && outputTargets->items.hasData())
|
||||
{
|
||||
auto targetArgIt = outputTargets->items.begin();
|
||||
const auto targetArgEnd = outputTargets->items.end();
|
||||
const auto positionalArgCount = outputTargets->items.getCount() -
|
||||
(dsqlOutputArgNames ? dsqlOutputArgNames->getCount() : 0);
|
||||
const auto* field = procedure->prc_outputs;
|
||||
unsigned pos = 0;
|
||||
|
||||
while (pos < positionalArgCount && field && targetArgIt != targetArgEnd)
|
||||
{
|
||||
if (const auto paramNode = nodeAs<ParameterNode>(*targetArgIt))
|
||||
{
|
||||
const auto parameter = paramNode->dsqlParameter = MAKE_parameter(
|
||||
dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, NULL);
|
||||
paramNode->dsqlParameterIndex = parameter->par_index;
|
||||
|
||||
DsqlDescMaker::fromField(¶meter->par_desc, field);
|
||||
parameter->par_name = parameter->par_alias = field->fld_name.c_str();
|
||||
parameter->par_rel_name = procedure->prc_name.identifier.c_str();
|
||||
parameter->par_owner_name = procedure->prc_owner.c_str();
|
||||
}
|
||||
|
||||
field = field->fld_next;
|
||||
++pos;
|
||||
++targetArgIt;
|
||||
}
|
||||
|
||||
if (dsqlOutputArgNames)
|
||||
{
|
||||
fb_assert(dsqlOutputArgNames->getCount() <= outputTargets->items.getCount());
|
||||
|
||||
LeftPooledMap<MetaName, const dsql_fld*> argsByName;
|
||||
|
||||
for (const auto* field = procedure->prc_outputs; field; field = field->fld_next)
|
||||
argsByName.put(field->fld_name, field);
|
||||
|
||||
Arg::StatusVector mismatchStatus;
|
||||
|
||||
for (const auto& argName : *dsqlOutputArgNames)
|
||||
{
|
||||
if (const auto field = argsByName.get(argName))
|
||||
{
|
||||
if (const auto paramNode = nodeAs<ParameterNode>(*targetArgIt))
|
||||
{
|
||||
const auto parameter = paramNode->dsqlParameter = MAKE_parameter(
|
||||
dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, NULL);
|
||||
paramNode->dsqlParameterIndex = parameter->par_index;
|
||||
|
||||
DsqlDescMaker::fromField(¶meter->par_desc, *field);
|
||||
parameter->par_name = parameter->par_alias = (*field)->fld_name.c_str();
|
||||
parameter->par_rel_name = procedure->prc_name.identifier.c_str();
|
||||
parameter->par_owner_name = procedure->prc_owner.c_str();
|
||||
}
|
||||
}
|
||||
else
|
||||
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
|
||||
|
||||
++targetArgIt;
|
||||
}
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_prcmismat) << dsqlName.toString() << mismatchStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto node = FB_NEW_POOL(pool) ExecProcedureNode(pool, dsqlName,
|
||||
doDsqlPass(dsqlScratch, inputSources),
|
||||
nullptr,
|
||||
dsqlInputArgNames ?
|
||||
FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool, *dsqlInputArgNames) :
|
||||
nullptr);
|
||||
|
||||
node->dsqlCallSyntax = dsqlCallSyntax;
|
||||
node->dsqlProcedure = procedure;
|
||||
|
||||
if (node->dsqlName.package.isEmpty() && procedure->prc_name.package.hasData())
|
||||
@ -3015,28 +3470,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()));
|
||||
|
||||
node->inputSources = doDsqlPass(dsqlScratch, inputSources);
|
||||
|
||||
if (count)
|
||||
if (node->inputSources && node->inputSources->items.hasData())
|
||||
{
|
||||
// Initialize this stack variable, and make it look like a node.
|
||||
dsc desc_node;
|
||||
auto sourceArgIt = node->inputSources->items.begin();
|
||||
const auto sourceArgEnd = node->inputSources->items.end();
|
||||
const auto positionalArgCount = node->inputSources->items.getCount() -
|
||||
(node->dsqlInputArgNames ? node->dsqlInputArgNames->getCount() : 0);
|
||||
const auto* field = procedure->prc_inputs;
|
||||
unsigned pos = 0;
|
||||
|
||||
NestConst<ValueExprNode>* ptr = node->inputSources->items.begin();
|
||||
const NestConst<ValueExprNode>* end = node->inputSources->items.end();
|
||||
|
||||
for (const dsql_fld* field = procedure->prc_inputs; ptr != end; ++ptr, field = field->fld_next)
|
||||
while (pos < positionalArgCount && field && sourceArgIt != sourceArgEnd)
|
||||
{
|
||||
DEV_BLKCHK(field, dsql_type_fld);
|
||||
DEV_BLKCHK(*ptr, dsql_type_nod);
|
||||
DsqlDescMaker::fromField(&desc_node, field);
|
||||
PASS1_set_parameter_type(dsqlScratch, *ptr,
|
||||
[&] (dsc* desc) { *desc = desc_node; },
|
||||
dsc descNode;
|
||||
DsqlDescMaker::fromField(&descNode, field);
|
||||
|
||||
PASS1_set_parameter_type(dsqlScratch, *sourceArgIt,
|
||||
[&] (dsc* desc) { *desc = descNode; },
|
||||
false);
|
||||
|
||||
field = field->fld_next;
|
||||
++pos;
|
||||
++sourceArgIt;
|
||||
}
|
||||
|
||||
if (node->dsqlInputArgNames)
|
||||
{
|
||||
fb_assert(node->dsqlInputArgNames->getCount() <= node->inputSources->items.getCount());
|
||||
|
||||
LeftPooledMap<MetaName, const dsql_fld*> argsByName;
|
||||
|
||||
for (const auto* field = procedure->prc_inputs; field; field = field->fld_next)
|
||||
argsByName.put(field->fld_name, field);
|
||||
|
||||
if (dsqlCallSyntax && dsqlScratch->isPsql())
|
||||
{
|
||||
for (const auto* field = procedure->prc_outputs; field; field = field->fld_next)
|
||||
argsByName.put(field->fld_name, field);
|
||||
}
|
||||
|
||||
Arg::StatusVector mismatchStatus;
|
||||
|
||||
for (const auto& argName : *node->dsqlInputArgNames)
|
||||
{
|
||||
if (const auto field = argsByName.get(argName))
|
||||
{
|
||||
dsc descNode;
|
||||
DsqlDescMaker::fromField(&descNode, *field);
|
||||
|
||||
PASS1_set_parameter_type(dsqlScratch, *sourceArgIt,
|
||||
[&] (dsc* desc) { *desc = descNode; },
|
||||
false);
|
||||
}
|
||||
else
|
||||
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
|
||||
|
||||
++sourceArgIt;
|
||||
}
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_prcmismat) << dsqlName.toString() << mismatchStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3044,16 +3536,19 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
|
||||
if (dsqlScratch->isPsql())
|
||||
{
|
||||
const USHORT outCount = outputSources ? outputSources->items.getCount() : 0;
|
||||
if (!dsqlCallSyntax)
|
||||
{
|
||||
const USHORT outCount = outputTargets ? outputTargets->items.getCount() : 0;
|
||||
|
||||
if (outCount != procedure->prc_out_count)
|
||||
ERRD_post(Arg::Gds(isc_prc_out_param_mismatch) << Arg::Str(dsqlName.toString()));
|
||||
if (outCount != procedure->prc_out_count)
|
||||
ERRD_post(Arg::Gds(isc_prc_out_param_mismatch) << Arg::Str(dsqlName.toString()));
|
||||
}
|
||||
|
||||
node->outputSources = dsqlPassArray(dsqlScratch, outputSources);
|
||||
node->outputTargets = dsqlPassArray(dsqlScratch, outputTargets);
|
||||
}
|
||||
else
|
||||
else if (!dsqlCallSyntax)
|
||||
{
|
||||
if (outputSources)
|
||||
if (outputTargets)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
||||
// Token unknown
|
||||
@ -3061,18 +3556,19 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
Arg::Gds(isc_random) << Arg::Str("RETURNING_VALUES"));
|
||||
}
|
||||
|
||||
node->outputSources = explodeOutputs(dsqlScratch, procedure);
|
||||
node->outputTargets = explodeOutputs(dsqlScratch, procedure);
|
||||
}
|
||||
else
|
||||
node->outputTargets = dsqlPassArray(dsqlScratch, outputTargets);
|
||||
|
||||
if (node->outputTargets)
|
||||
{
|
||||
for (const auto target : node->outputTargets->items)
|
||||
AssignmentNode::dsqlValidateTarget(target);
|
||||
}
|
||||
|
||||
if (node->outputSources)
|
||||
{
|
||||
for (const NestConst<ValueExprNode>* i = node->outputSources->items.begin();
|
||||
i != node->outputSources->items.end();
|
||||
++i)
|
||||
{
|
||||
AssignmentNode::dsqlValidateTarget(*i);
|
||||
}
|
||||
}
|
||||
if (dsqlOutputArgNames)
|
||||
node->dsqlOutputArgNames = FB_NEW_POOL(pool) ObjectsArray<MetaName>(pool, *dsqlOutputArgNames);
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -3092,10 +3588,10 @@ ValueListNode* ExecProcedureNode::explodeOutputs(DsqlCompilerScratch* dsqlScratc
|
||||
{
|
||||
DEV_BLKCHK(field, dsql_type_fld);
|
||||
|
||||
ParameterNode* paramNode = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
|
||||
const auto paramNode = FB_NEW_POOL(dsqlScratch->getPool()) ParameterNode(dsqlScratch->getPool());
|
||||
*ptr = paramNode;
|
||||
|
||||
dsql_par* parameter = paramNode->dsqlParameter = MAKE_parameter(
|
||||
const auto parameter = paramNode->dsqlParameter = MAKE_parameter(
|
||||
dsqlScratch->getDsqlStatement()->getReceiveMsg(), true, true, 0, NULL);
|
||||
paramNode->dsqlParameterIndex = parameter->par_index;
|
||||
|
||||
@ -3125,7 +3621,7 @@ string ExecProcedureNode::internalPrint(NodePrinter& printer) const
|
||||
|
||||
void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
const dsql_msg* message = NULL;
|
||||
const dsql_msg* message = nullptr;
|
||||
|
||||
if (dsqlScratch->getDsqlStatement()->getType() == DsqlStatement::TYPE_EXEC_PROCEDURE)
|
||||
{
|
||||
@ -3137,44 +3633,104 @@ void ExecProcedureNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
}
|
||||
}
|
||||
|
||||
if (dsqlName.package.hasData())
|
||||
if (dsqlInputArgNames || dsqlOutputArgNames || dsqlCallSyntax)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_exec_proc2);
|
||||
dsqlScratch->appendMetaString(dsqlName.package.c_str());
|
||||
dsqlScratch->appendUChar(blr_invoke_procedure);
|
||||
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_type);
|
||||
|
||||
if (dsqlName.package.hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_type_packaged);
|
||||
dsqlScratch->appendMetaString(dsqlName.package.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ?
|
||||
blr_invsel_procedure_type_sub : blr_invsel_procedure_type_standalone);
|
||||
}
|
||||
|
||||
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
|
||||
|
||||
const bool useInOut = dsqlScratch->isPsql() && dsqlCallSyntax;
|
||||
|
||||
// Input parameters.
|
||||
if (inputSources)
|
||||
{
|
||||
if (dsqlInputArgNames && dsqlInputArgNames->hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(
|
||||
useInOut ? blr_invsel_procedure_inout_arg_names : blr_invsel_procedure_in_arg_names);
|
||||
dsqlScratch->appendUShort(dsqlInputArgNames->getCount());
|
||||
|
||||
for (auto& argName : *dsqlInputArgNames)
|
||||
dsqlScratch->appendMetaString(argName.c_str());
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar(
|
||||
useInOut ? blr_invsel_procedure_inout_args : blr_invsel_procedure_in_args);
|
||||
dsqlScratch->appendUShort(inputSources->items.getCount());
|
||||
|
||||
for (auto& arg : inputSources->items)
|
||||
GEN_arg(dsqlScratch, arg);
|
||||
}
|
||||
|
||||
// Output parameters.
|
||||
if (!useInOut && outputTargets)
|
||||
{
|
||||
if (dsqlOutputArgNames && dsqlOutputArgNames->hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_out_arg_names);
|
||||
dsqlScratch->appendUShort(dsqlOutputArgNames->getCount());
|
||||
|
||||
for (auto& argName : *dsqlOutputArgNames)
|
||||
dsqlScratch->appendMetaString(argName.c_str());
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar(blr_invsel_procedure_out_args);
|
||||
dsqlScratch->appendUShort(outputTargets->items.getCount());
|
||||
|
||||
for (auto& arg : outputTargets->items)
|
||||
GEN_arg(dsqlScratch, arg);
|
||||
}
|
||||
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsqlScratch->appendUChar(
|
||||
(dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc);
|
||||
if (dsqlName.package.hasData())
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_exec_proc2);
|
||||
dsqlScratch->appendMetaString(dsqlName.package.c_str());
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUChar((dsqlProcedure->prc_flags & PRC_subproc) ? blr_exec_subproc : blr_exec_proc);
|
||||
|
||||
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
|
||||
|
||||
// Input parameters.
|
||||
if (inputSources)
|
||||
{
|
||||
dsqlScratch->appendUShort(inputSources->items.getCount());
|
||||
|
||||
for (auto& arg : inputSources->items)
|
||||
GEN_arg(dsqlScratch, arg);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
|
||||
// Output parameters.
|
||||
if (outputTargets)
|
||||
{
|
||||
dsqlScratch->appendUShort(outputTargets->items.getCount());
|
||||
|
||||
for (auto& arg : outputTargets->items)
|
||||
GEN_expr(dsqlScratch, arg);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
}
|
||||
|
||||
dsqlScratch->appendMetaString(dsqlName.identifier.c_str());
|
||||
|
||||
// Input parameters.
|
||||
if (inputSources)
|
||||
{
|
||||
dsqlScratch->appendUShort(inputSources->items.getCount());
|
||||
NestConst<ValueExprNode>* ptr = inputSources->items.begin();
|
||||
const NestConst<ValueExprNode>* end = inputSources->items.end();
|
||||
|
||||
while (ptr < end)
|
||||
GEN_expr(dsqlScratch, *ptr++);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
|
||||
// Output parameters.
|
||||
if (outputSources)
|
||||
{
|
||||
dsqlScratch->appendUShort(outputSources->items.getCount());
|
||||
NestConst<ValueExprNode>* ptr = outputSources->items.begin();
|
||||
|
||||
for (const NestConst<ValueExprNode>* end = outputSources->items.end(); ptr != end; ++ptr)
|
||||
GEN_expr(dsqlScratch, *ptr);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
|
||||
if (message)
|
||||
dsqlScratch->appendUChar(blr_end);
|
||||
}
|
||||
@ -3209,12 +3765,8 @@ ExecProcedureNode* ExecProcedureNode::pass2(thread_db* tdbb, CompilerScratch* cs
|
||||
|
||||
if (outputTargets)
|
||||
{
|
||||
for (const NestConst<ValueExprNode>* i = outputTargets->items.begin();
|
||||
i != outputTargets->items.end();
|
||||
++i)
|
||||
{
|
||||
AssignmentNode::validateTarget(csb, *i);
|
||||
}
|
||||
for (const auto target : outputTargets->items)
|
||||
AssignmentNode::validateTarget(csb, target);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -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)
|
||||
outputTargets(aOutputs),
|
||||
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,9 @@ public:
|
||||
NestConst<ValueListNode> outputTargets;
|
||||
NestConst<MessageNode> outputMessage;
|
||||
NestConst<jrd_prc> procedure;
|
||||
NestConst<Firebird::ObjectsArray<MetaName>> dsqlInputArgNames;
|
||||
NestConst<Firebird::ObjectsArray<MetaName>> dsqlOutputArgNames;
|
||||
bool dsqlCallSyntax = false;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1127,9 +1127,8 @@ static UCHAR* var_info(const dsql_msg* message,
|
||||
for (FB_SIZE_T i = 0; i < parameters.getCount(); i++)
|
||||
{
|
||||
const dsql_par* param = parameters[i];
|
||||
fb_assert(param);
|
||||
|
||||
if (param->par_index >= first_index)
|
||||
if (param && param->par_index >= first_index)
|
||||
{
|
||||
dsc desc = param->par_desc;
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -65,6 +65,17 @@ using namespace Firebird;
|
||||
static void gen_plan(DsqlCompilerScratch*, const PlanNode*);
|
||||
|
||||
|
||||
// Generate blr for an argument.
|
||||
// When it is nullptr, generate blr_default_arg.
|
||||
void GEN_arg(DsqlCompilerScratch* dsqlScratch, ExprNode* node)
|
||||
{
|
||||
if (node)
|
||||
GEN_expr(dsqlScratch, node);
|
||||
else
|
||||
dsqlScratch->appendUChar(blr_default_arg);
|
||||
}
|
||||
|
||||
|
||||
void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
/**************************************
|
||||
@ -159,9 +170,24 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message)
|
||||
DSqlDataTypeUtil dataTypeUtil(dsqlScratch);
|
||||
ULONG offset = 0;
|
||||
|
||||
class ParamCmp
|
||||
{
|
||||
public:
|
||||
static int greaterThan(const Jrd::dsql_par* p1, const Jrd::dsql_par* p2)
|
||||
{
|
||||
return p1->par_index > p2->par_index;
|
||||
}
|
||||
};
|
||||
|
||||
SortedArray<dsql_par*, InlineStorage<dsql_par*, 16>, dsql_par*,
|
||||
DefaultKeyValue<dsql_par*>, ParamCmp> dsqlParams;
|
||||
|
||||
for (FB_SIZE_T i = 0; i < message->msg_parameters.getCount(); ++i)
|
||||
{
|
||||
dsql_par* parameter = message->msg_parameters[i];
|
||||
const auto parameter = message->msg_parameters[i];
|
||||
|
||||
if (parameter->par_index)
|
||||
dsqlParams.add(parameter);
|
||||
|
||||
parameter->par_parameter = (USHORT) i;
|
||||
|
||||
@ -221,6 +247,13 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message)
|
||||
message->msg_length = offset;
|
||||
|
||||
dsqlScratch->getDsqlStatement()->getPorts().add(message);
|
||||
|
||||
// Remove gaps in par_index due to output parameters using question-marks (CALL syntax).
|
||||
|
||||
USHORT parIndex = 0;
|
||||
|
||||
for (auto dsqlParam : dsqlParams)
|
||||
dsqlParam->par_index = ++parIndex;
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@ namespace Jrd
|
||||
class ValueListNode;
|
||||
}
|
||||
|
||||
void GEN_arg(Jrd::DsqlCompilerScratch*, Jrd::ExprNode*);
|
||||
void GEN_descriptor(Jrd::DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool texttype);
|
||||
void GEN_expr(Jrd::DsqlCompilerScratch*, Jrd::ExprNode*);
|
||||
void GEN_hidden_variables(Jrd::DsqlCompilerScratch* dsqlScratch);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
148
src/dsql/parse.y
148
src/dsql/parse.y
@ -699,6 +699,8 @@ using namespace Firebird;
|
||||
// tokens added for Firebird 6.0
|
||||
|
||||
%token <metaNamePtr> ANY_VALUE
|
||||
%token <metaNamePtr> CALL
|
||||
%token <metaNamePtr> NAMED_ARG_ASSIGN
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
@ -761,6 +763,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;
|
||||
@ -887,6 +891,7 @@ dml_statement
|
||||
| insert { $$ = $1; }
|
||||
| merge { $$ = $1; }
|
||||
| exec_procedure { $$ = $1; }
|
||||
| call { $$ = $1; }
|
||||
| exec_block { $$ = $1; }
|
||||
| select { $$ = $1; }
|
||||
| update { $$ = $1; }
|
||||
@ -3275,6 +3280,7 @@ simple_proc_statement
|
||||
| delete
|
||||
| singleton_select
|
||||
| exec_procedure
|
||||
| call { $$ = $1; }
|
||||
| exec_sql { $$ = $1; }
|
||||
| exec_into { $$ = $1; }
|
||||
| exec_function
|
||||
@ -3753,16 +3759,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
|
||||
@ -3772,6 +3790,31 @@ proc_outputs_opt
|
||||
| RETURNING_VALUES '(' variable_list ')' { $$ = $3; }
|
||||
;
|
||||
|
||||
// CALL
|
||||
|
||||
%type <stmtNode> call
|
||||
call
|
||||
: CALL symbol_procedure_name '(' argument_list_opt ')'
|
||||
{
|
||||
auto node = newNode<ExecProcedureNode>(QualifiedName(*$2),
|
||||
($4 ? $4->second : nullptr),
|
||||
nullptr,
|
||||
($4 ? $4->first : nullptr));
|
||||
node->dsqlCallSyntax = true;
|
||||
$$ = node;
|
||||
}
|
||||
| CALL symbol_package_name '.' symbol_procedure_name '(' argument_list_opt ')'
|
||||
into_variable_list_opt
|
||||
{
|
||||
auto node = newNode<ExecProcedureNode>(QualifiedName(*$4, *$2),
|
||||
($6 ? $6->second : nullptr),
|
||||
nullptr,
|
||||
($6 ? $6->first : nullptr));
|
||||
node->dsqlCallSyntax = true;
|
||||
$$ = node;
|
||||
}
|
||||
;
|
||||
|
||||
// EXECUTE BLOCK
|
||||
|
||||
%type <execBlockNode> exec_block
|
||||
@ -4361,6 +4404,7 @@ keyword_or_column
|
||||
| VARBINARY
|
||||
| WINDOW
|
||||
| WITHOUT
|
||||
| CALL // added in FB 6.0
|
||||
;
|
||||
|
||||
col_opt
|
||||
@ -6248,38 +6292,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 +8628,64 @@ 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
|
||||
| value_or_default_list
|
||||
{
|
||||
$$ = newNode<NonPooledPair<ObjectsArray<MetaName>*, ValueListNode*>>();
|
||||
$$->second = $1;
|
||||
}
|
||||
| value_or_default_list ',' named_argument_list
|
||||
{
|
||||
$$ = $3;
|
||||
|
||||
for (auto item : $$->second->items)
|
||||
$1->add(item);
|
||||
|
||||
delete $$->second;
|
||||
$$->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_or_default
|
||||
{ $$ = 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,34 +529,73 @@ 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();
|
||||
}
|
||||
|
||||
if (count > procedure->prc_in_count ||
|
||||
count < procedure->prc_in_count - procedure->prc_def_count)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_prcmismat) << Arg::Str(procNode->dsqlName.toString()));
|
||||
ERRD_post(Arg::Gds(isc_prcmismat) << procNode->dsqlName.toString());
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
// 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 argIt = inputList->items.begin();
|
||||
const auto argEnd = inputList->items.end();
|
||||
|
||||
for (dsql_fld* field = procedure->prc_inputs;
|
||||
input != inputList->items.end();
|
||||
++input, field = field->fld_next)
|
||||
const auto positionalArgCount = inputList->items.getCount() -
|
||||
(procNode->dsqlInputArgNames ? procNode->dsqlInputArgNames->getCount() : 0);
|
||||
const auto* field = procedure->prc_inputs;
|
||||
unsigned pos = 0;
|
||||
|
||||
while (pos < positionalArgCount && field && argIt != argEnd)
|
||||
{
|
||||
DEV_BLKCHK(field, dsql_type_fld);
|
||||
DsqlDescMaker::fromField(&desc_node, field);
|
||||
PASS1_set_parameter_type(dsqlScratch, *input,
|
||||
[&] (dsc* desc) { *desc = desc_node; },
|
||||
dsc descNode;
|
||||
DsqlDescMaker::fromField(&descNode, field);
|
||||
|
||||
PASS1_set_parameter_type(dsqlScratch, *argIt,
|
||||
[&] (dsc* desc) { *desc = descNode; },
|
||||
false);
|
||||
|
||||
field = field->fld_next;
|
||||
++pos;
|
||||
++argIt;
|
||||
}
|
||||
|
||||
if (procNode->dsqlInputArgNames)
|
||||
{
|
||||
fb_assert(procNode->dsqlInputArgNames->getCount() <= inputList->items.getCount());
|
||||
|
||||
LeftPooledMap<MetaName, const dsql_fld*> argsByName;
|
||||
|
||||
for (const auto* field = procedure->prc_inputs; field; field = field->fld_next)
|
||||
argsByName.put(field->fld_name, field);
|
||||
|
||||
Arg::StatusVector mismatchStatus;
|
||||
|
||||
for (const auto& argName : *procNode->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
|
||||
mismatchStatus << Arg::Gds(isc_param_not_exist) << argName;
|
||||
|
||||
++argIt;
|
||||
}
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_prcmismat) << procNode->dsqlName.toString() << mismatchStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1677,21 +1710,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,33 @@
|
||||
|
||||
#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_inout_arg_names (unsigned char) 6
|
||||
#define blr_invsel_procedure_inout_args (unsigned char) 7
|
||||
#define blr_invsel_procedure_context (unsigned char) 8
|
||||
#define blr_invsel_procedure_alias (unsigned char) 9
|
||||
|
||||
#define blr_default_arg (unsigned char) 227
|
||||
|
||||
#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 or was specified with DEFAULT")
|
||||
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,
|
||||
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,108 @@ 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 (inArgNames && inArgNames->getCount() > node->inputSources->items.getCount())
|
||||
{
|
||||
if (procedure->isImplemented() && !procedure->isDefined())
|
||||
blrReader.setPos(inArgNamesPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_random) <<
|
||||
"blr_invsel_procedure_in_arg_names count cannot be greater than blr_invsel_procedure_in_args");
|
||||
}
|
||||
|
||||
if (!node->procedure)
|
||||
{
|
||||
blrReader.setPos(blrStartPos);
|
||||
PAR_error(csb, Arg::Gds(isc_prcnotdef) << name.toString());
|
||||
}
|
||||
|
||||
if (node->procedure->prc_type == prc_executable)
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak())
|
||||
PAR_warning(Arg::Warning(isc_illegal_prc_type) << node->procedure->getName().toString());
|
||||
else
|
||||
PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << node->procedure->getName().toString());
|
||||
}
|
||||
|
||||
node->isSubRoutine = node->procedure->isSubRoutine();
|
||||
node->procedureId = node->isSubRoutine ? 0 : node->procedure->getId();
|
||||
|
||||
if (node->procedure->isImplemented() && !node->procedure->isDefined())
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
{
|
||||
if (tdbb->getAttachment()->isGbak() || (tdbb->tdbb_flags & TDBB_replicator))
|
||||
{
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
PAR_warning(
|
||||
Arg::Warning(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Warning(isc_modnotfound));
|
||||
}
|
||||
else
|
||||
{
|
||||
blrReader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << name.toString() <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
}
|
||||
}
|
||||
|
||||
if (parseContext)
|
||||
{
|
||||
if (blrOp != blr_select_procedure)
|
||||
{
|
||||
node->stream = PAR_context(csb, &node->context);
|
||||
|
||||
csb->csb_rpt[node->stream].csb_procedure = node->procedure;
|
||||
csb->csb_rpt[node->stream].csb_alias = &node->alias;
|
||||
|
||||
inArgCount = blrReader.getWord();
|
||||
node->inputSources = PAR_args(tdbb, csb, inArgCount, inArgCount);
|
||||
}
|
||||
|
||||
if (!node->inputSources)
|
||||
node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool);
|
||||
|
||||
node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount());
|
||||
|
||||
Arg::StatusVector mismatchStatus= CMP_procedure_arguments(
|
||||
tdbb,
|
||||
csb,
|
||||
node->procedure,
|
||||
true,
|
||||
inArgCount,
|
||||
inArgNames,
|
||||
node->inputSources,
|
||||
node->inputTargets,
|
||||
node->inputMessage);
|
||||
|
||||
if (mismatchStatus.hasData())
|
||||
status_exception::raise(Arg::Gds(isc_prcmismat) << node->procedure->getName().toString() << mismatchStatus);
|
||||
|
||||
if (csb->collectingDependencies() && !node->procedure->isSubRoutine())
|
||||
{
|
||||
{ // scope
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = node->procedure;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
else
|
||||
|
||||
if (inArgNames)
|
||||
{
|
||||
csb->csb_blr_reader.setPos(blrStartPos);
|
||||
PAR_error(csb,
|
||||
Arg::Gds(isc_prcnotdef) << Arg::Str(name.toString()) <<
|
||||
Arg::Gds(isc_modnotfound));
|
||||
for (const auto& argName : *inArgNames)
|
||||
{
|
||||
CompilerScratch::Dependency dependency(obj_procedure);
|
||||
dependency.procedure = node->procedure;
|
||||
dependency.subName = &argName;
|
||||
csb->addDependency(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (procedure->prc_type == prc_executable)
|
||||
if (node->inputSources && node->inputSources->items.isEmpty())
|
||||
{
|
||||
const string name = procedure->getName().toString();
|
||||
delete node->inputSources.getObject();
|
||||
node->inputSources = nullptr;
|
||||
|
||||
if (tdbb->getAttachment()->isGbak())
|
||||
PAR_warning(Arg::Warning(isc_illegal_prc_type) << Arg::Str(name));
|
||||
else
|
||||
PAR_error(csb, Arg::Gds(isc_illegal_prc_type) << Arg::Str(name));
|
||||
}
|
||||
|
||||
ProcedureSourceNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ProcedureSourceNode(
|
||||
*tdbb->getDefaultPool());
|
||||
|
||||
node->procedure = procedure;
|
||||
node->isSubRoutine = procedure->isSubRoutine();
|
||||
node->procedureId = node->isSubRoutine ? 0 : procedure->getId();
|
||||
|
||||
if (aliasString)
|
||||
node->alias = *aliasString;
|
||||
|
||||
if (parseContext)
|
||||
{
|
||||
node->stream = PAR_context(csb, &node->context);
|
||||
|
||||
csb->csb_rpt[node->stream].csb_procedure = procedure;
|
||||
csb->csb_rpt[node->stream].csb_alias = aliasString.release();
|
||||
|
||||
PAR_procedure_parms(tdbb, csb, procedure, node->in_msg.getAddress(),
|
||||
node->sourceList.getAddress(), node->targetList.getAddress(), true);
|
||||
|
||||
if (csb->collectingDependencies())
|
||||
PAR_dependency(tdbb, csb, node->stream, (SSHORT) -1, "");
|
||||
delete node->inputTargets.getObject();
|
||||
node->inputTargets = nullptr;
|
||||
}
|
||||
|
||||
return node;
|
||||
@ -1023,7 +1180,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 +1197,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 +1206,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 +1215,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 +1224,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 +1233,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 +1242,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 +1257,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_arg(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 +1327,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 +1350,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_arg(dsqlScratch, arg);
|
||||
}
|
||||
else
|
||||
dsqlScratch->appendUShort(0);
|
||||
@ -1180,12 +1386,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 +1417,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 +1462,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 +1481,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 +1499,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 +1912,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 +3658,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,9 @@ 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},
|
||||
{"blr_default_arg", zero},
|
||||
{0, 0}
|
||||
};
|
||||
|
155
src/jrd/cmp.cpp
155
src/jrd/cmp.cpp
@ -459,6 +459,161 @@ 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);
|
||||
|
||||
// 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 ---
|
||||
|
||||
const auto positionalArgCount = argNames ? argCount - argNames->getCount() : argCount;
|
||||
auto sourceArgIt = sources->items.begin();
|
||||
LeftPooledMap<MetaName, NestConst<ValueExprNode>> argsByName;
|
||||
|
||||
if (positionalArgCount)
|
||||
{
|
||||
if (argCount > fields.getCount())
|
||||
mismatchStatus << Arg::Gds(isc_wronumarg);
|
||||
|
||||
for (auto pos = 0; pos < positionalArgCount; ++pos)
|
||||
{
|
||||
if (pos < fields.getCount())
|
||||
{
|
||||
const auto& parameter = fields[pos];
|
||||
|
||||
if (argsByName.put(parameter->prm_name, *sourceArgIt))
|
||||
mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << parameter->prm_name;
|
||||
}
|
||||
|
||||
++sourceArgIt;
|
||||
}
|
||||
}
|
||||
|
||||
if (argNames)
|
||||
{
|
||||
for (const auto& argName : *argNames)
|
||||
{
|
||||
if (argsByName.put(argName, *sourceArgIt++))
|
||||
mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << argName;
|
||||
}
|
||||
}
|
||||
|
||||
sourceArgIt = sources->items.begin();
|
||||
auto targetArgIt = targets->items.begin();
|
||||
|
||||
for (auto& parameter : fields)
|
||||
{
|
||||
const auto argValue = argsByName.get(parameter->prm_name);
|
||||
|
||||
if (argValue)
|
||||
{
|
||||
*sourceArgIt = *argValue;
|
||||
argsByName.remove(parameter->prm_name);
|
||||
}
|
||||
|
||||
if (isInput && (!argValue || !*argValue))
|
||||
{
|
||||
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 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
|
||||
|
115
src/jrd/met.epp
115
src/jrd/met.epp
@ -685,109 +685,66 @@ void MET_clear_cache(thread_db* tdbb)
|
||||
}
|
||||
}
|
||||
|
||||
const auto walkProcFunc = [&att](std::function<void (Routine*)> func)
|
||||
{
|
||||
for (const auto routine : att->att_procedures)
|
||||
{
|
||||
if (routine)
|
||||
func(routine);
|
||||
}
|
||||
|
||||
for (const auto routine : att->att_functions)
|
||||
{
|
||||
if (routine)
|
||||
func(routine);
|
||||
}
|
||||
};
|
||||
|
||||
// Walk routines and calculate internal dependencies.
|
||||
|
||||
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
||||
walkProcFunc([](Routine* routine)
|
||||
{
|
||||
Routine* const routine = *iter;
|
||||
|
||||
if (routine && routine->getStatement() &&
|
||||
if (routine->getStatement() &&
|
||||
!(routine->flags & Routine::FLAG_OBSOLETE) )
|
||||
{
|
||||
inc_int_use_count(routine->getStatement());
|
||||
}
|
||||
}
|
||||
|
||||
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
||||
{
|
||||
Function* const routine = *iter;
|
||||
|
||||
if (routine && routine->getStatement() &&
|
||||
!(routine->flags & Routine::FLAG_OBSOLETE) )
|
||||
{
|
||||
inc_int_use_count(routine->getStatement());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Walk routines again and adjust dependencies for routines which will not be removed.
|
||||
|
||||
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
||||
walkProcFunc([](Routine* routine)
|
||||
{
|
||||
Routine* const routine = *iter;
|
||||
|
||||
if (routine && routine->getStatement() &&
|
||||
if (routine->getStatement() &&
|
||||
!(routine->flags & Routine::FLAG_OBSOLETE) &&
|
||||
routine->useCount != routine->intUseCount )
|
||||
{
|
||||
adjust_dependencies(routine);
|
||||
}
|
||||
}
|
||||
|
||||
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
||||
{
|
||||
Function* const routine = *iter;
|
||||
|
||||
if (routine && routine->getStatement() &&
|
||||
!(routine->flags & Routine::FLAG_OBSOLETE) &&
|
||||
routine->useCount != routine->intUseCount )
|
||||
{
|
||||
adjust_dependencies(routine);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Deallocate all used requests.
|
||||
|
||||
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
||||
walkProcFunc([&tdbb](Routine* routine)
|
||||
{
|
||||
Routine* const routine = *iter;
|
||||
|
||||
if (routine)
|
||||
if (routine->getStatement() && !(routine->flags & Routine::FLAG_OBSOLETE) &&
|
||||
routine->intUseCount >= 0 &&
|
||||
routine->useCount == routine->intUseCount)
|
||||
{
|
||||
if (routine->getStatement() && !(routine->flags & Routine::FLAG_OBSOLETE) &&
|
||||
routine->intUseCount >= 0 &&
|
||||
routine->useCount == routine->intUseCount)
|
||||
{
|
||||
routine->releaseStatement(tdbb);
|
||||
routine->releaseStatement(tdbb);
|
||||
|
||||
if (routine->existenceLock)
|
||||
LCK_release(tdbb, routine->existenceLock);
|
||||
routine->existenceLock = NULL;
|
||||
routine->flags |= Routine::FLAG_OBSOLETE;
|
||||
}
|
||||
|
||||
// Leave it in state 0 to avoid extra pass next time to clear it
|
||||
// Note: we need to adjust intUseCount for all routines
|
||||
// in cache because any of them may have been affected from
|
||||
// dependencies earlier. Even routines that were not scanned yet !
|
||||
routine->intUseCount = 0;
|
||||
if (routine->existenceLock)
|
||||
LCK_release(tdbb, routine->existenceLock);
|
||||
routine->existenceLock = NULL;
|
||||
routine->flags |= Routine::FLAG_OBSOLETE;
|
||||
}
|
||||
}
|
||||
|
||||
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
||||
{
|
||||
Function* const routine = *iter;
|
||||
|
||||
if (routine)
|
||||
{
|
||||
if (routine->getStatement() && !(routine->flags & Routine::FLAG_OBSOLETE) &&
|
||||
routine->intUseCount >= 0 &&
|
||||
routine->useCount == routine->intUseCount)
|
||||
{
|
||||
routine->releaseStatement(tdbb);
|
||||
|
||||
if (routine->existenceLock)
|
||||
LCK_release(tdbb, routine->existenceLock);
|
||||
routine->existenceLock = NULL;
|
||||
routine->flags |= Routine::FLAG_OBSOLETE;
|
||||
}
|
||||
|
||||
// Leave it in state 0 to avoid extra pass next time to clear it
|
||||
// Note: we need to adjust intUseCount for all routines
|
||||
// in cache because any of them may have been affected from
|
||||
// dependencies earlier. Even routines that were not scanned yet !
|
||||
routine->intUseCount = 0;
|
||||
}
|
||||
}
|
||||
// Leave it in state 0 to avoid extra pass next time to clear it
|
||||
// Note: we need to adjust intUseCount for all routines
|
||||
// in cache because any of them may have been affected from
|
||||
// dependencies earlier. Even routines that were not scanned yet !
|
||||
routine->intUseCount = 0;
|
||||
});
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
MET_verify_cache(tdbb);
|
||||
|
177
src/jrd/par.cpp
177
src/jrd/par.cpp
@ -804,7 +804,12 @@ ValueListNode* PAR_args(thread_db* tdbb, CompilerScratch* csb, USHORT count, USH
|
||||
{
|
||||
do
|
||||
{
|
||||
*ptr++ = PAR_parse_value(tdbb, csb);
|
||||
if (csb->csb_blr_reader.peekByte() == blr_default_arg)
|
||||
csb->csb_blr_reader.getByte();
|
||||
else
|
||||
*ptr = PAR_parse_value(tdbb, csb);
|
||||
|
||||
++ptr;
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
@ -821,28 +826,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 +855,28 @@ StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Introduce a new context into the system - byte version.
|
||||
StreamType PAR_context(CompilerScratch* csb, SSHORT* context_ptr)
|
||||
{
|
||||
const USHORT context = csb->csb_blr_reader.getByte();
|
||||
|
||||
if (context_ptr)
|
||||
*context_ptr = (SSHORT) context;
|
||||
|
||||
return par_context(csb, context);
|
||||
}
|
||||
|
||||
// Introduce a new context into the system - word version.
|
||||
StreamType PAR_context2(CompilerScratch* csb, SSHORT* context_ptr)
|
||||
{
|
||||
const USHORT context = csb->csb_blr_reader.getWord();
|
||||
|
||||
if (context_ptr)
|
||||
*context_ptr = (SSHORT) context;
|
||||
|
||||
return par_context(csb, context);
|
||||
}
|
||||
|
||||
|
||||
void PAR_dependency(thread_db* tdbb, CompilerScratch* csb, StreamType stream, SSHORT id,
|
||||
const MetaName& field_name)
|
||||
@ -975,12 +985,12 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
case blr_rid:
|
||||
case blr_relation2:
|
||||
case blr_rid2:
|
||||
{
|
||||
const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false);
|
||||
plan->recordSourceNode = relationNode;
|
||||
relation = relationNode->relation;
|
||||
}
|
||||
{
|
||||
const auto relationNode = RelationSourceNode::parse(tdbb, csb, blrOp, false);
|
||||
plan->recordSourceNode = relationNode;
|
||||
relation = relationNode->relation;
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_pid:
|
||||
case blr_pid2:
|
||||
@ -989,12 +999,13 @@ static PlanNode* par_plan(thread_db* tdbb, CompilerScratch* csb)
|
||||
case blr_procedure3:
|
||||
case blr_procedure4:
|
||||
case blr_subproc:
|
||||
{
|
||||
const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false);
|
||||
plan->recordSourceNode = procedureNode;
|
||||
procedure = procedureNode->procedure;
|
||||
}
|
||||
case blr_select_procedure:
|
||||
{
|
||||
const auto procedureNode = ProcedureSourceNode::parse(tdbb, csb, blrOp, false);
|
||||
plan->recordSourceNode = procedureNode;
|
||||
procedure = procedureNode->procedure;
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_local_table_id:
|
||||
// TODO
|
||||
@ -1010,7 +1021,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 +1178,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 +1194,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 +1552,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:
|
||||
|
@ -39,6 +39,8 @@ namespace Jrd {
|
||||
class StmtNode;
|
||||
class ValueExprNode;
|
||||
class ValueListNode;
|
||||
|
||||
using NodeParseFunc = DmlNode* (*)(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
|
||||
}
|
||||
|
||||
struct dsc;
|
||||
@ -52,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*);
|
||||
@ -66,22 +69,17 @@ 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);
|
||||
Jrd::SortNode* PAR_sort_internal(Jrd::thread_db*, Jrd::CompilerScratch*, bool, USHORT);
|
||||
SLONG PAR_symbol_to_gdscode(const Firebird::string&);
|
||||
|
||||
typedef Jrd::DmlNode* (*NodeParseFunc)(Jrd::thread_db* tdbb, MemoryPool& pool,
|
||||
Jrd::CompilerScratch* csb, const UCHAR blrOp);
|
||||
|
||||
Jrd::BoolExprNode* PAR_parse_boolean(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb);
|
||||
Jrd::ValueExprNode* PAR_parse_value(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb);
|
||||
Jrd::StmtNode* PAR_parse_stmt(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb);
|
||||
Jrd::DmlNode* PAR_parse_node(Jrd::thread_db* tdbb, Jrd::CompilerScratch* csb);
|
||||
void PAR_register(UCHAR blr, NodeParseFunc parseFunc);
|
||||
void PAR_register(UCHAR blr, Jrd::NodeParseFunc parseFunc);
|
||||
void PAR_syntax_error(Jrd::CompilerScratch* csb, const TEXT* string);
|
||||
void PAR_warning(const Firebird::Arg::StatusVector& v);
|
||||
|
||||
|
@ -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,210 @@ 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",
|
||||
"inout_arg_names",
|
||||
"inout_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:
|
||||
case blr_invsel_procedure_inout_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:
|
||||
case blr_invsel_procedure_inout_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