Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 88 additions & 20 deletions src/dsql/DdlNodes.epp
Original file line number Diff line number Diff line change
Expand Up @@ -1631,23 +1631,102 @@ DdlNode* CreateAlterFunctionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
{
dsqlScratch->flags |= (DsqlCompilerScratch::FLAG_BLOCK | DsqlCompilerScratch::FLAG_FUNCTION);

if (this->isUdf())
{
// check udfReturnPos
const dsql_fld* const field = this->returnType ? this->returnType->type : NULL;

if (field)
{
// CVC: This is case of "returns <type> [by value|reference]".
fb_assert(udfReturnPos == 0);
}
else
{
// CVC: This is case of "returns parameter <N>"

// see return_value in parser
fb_assert(udfReturnPos > 0);

if (udfReturnPos < 1 || ULONG(udfReturnPos) > parameters.getCount())
{
// CVC: We should devise new msg "position should be between 1 and #params";
// here it is: dsql_udf_return_pos_err

// External functions can not have more than 10 parameters
// Not strictly correct -- return position error
status_exception::raise(
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
Arg::Gds(isc_dsql_command_err) << // gds__extern_func_err
Arg::Gds(isc_dsql_udf_return_pos_err) << Arg::Num(parameters.getCount()));
}
}

fb_assert(udfReturnPos >= 0 && ULONG(udfReturnPos) <= parameters.getCount());
}

// check for duplicated parameters and declaration names

StrArray names;

for (FB_SIZE_T i = 0; i < parameters.getCount(); ++i)
if (this->isUdf())
{
const ParameterClause* const parameter = parameters[i];
// Generation of UDF input argument names.
// They will be used for work with SCALAR_ARRAY-arguments.

if (names.exist(parameter->name.c_str()))
unsigned argNum = 0;

for (FB_SIZE_T i = 0; i < parameters.getCount(); ++i)
{
status_exception::raise(
Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(parameter->name));
ParameterClause* const parameter = parameters[i];

fb_assert(!parameter->name.hasData());

if (this->udfReturnPos == (i + 1))
{
// It is a parameter that is marked as RETURN

fb_assert(this->returnType == nullptr); // research. it is assumed.
fb_assert(this->udfReturnPos > 0); // paranoia

fb_assert(!names.exist(parameter->name.c_str()));
continue;
}

// It is an input argument
++argNum;
parameter->name.printf("ARG%d", argNum);
fb_assert(parameter->name.hasData());

if (names.exist(parameter->name.c_str()))
{
status_exception::raise(
Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(parameter->name));
}

names.add(parameter->name.c_str());
}
}
else
{
fb_assert(!this->isUdf());

for (FB_SIZE_T i = 0; i < parameters.getCount(); ++i)
{
const ParameterClause* const parameter = parameters[i];

fb_assert(parameter->name.hasData());

if (names.exist(parameter->name.c_str()))
{
status_exception::raise(
Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(parameter->name));
}

if (parameter->name.hasData()) // legacy UDFs has unnamed parameters
names.add(parameter->name.c_str());
}
}

PASS1_check_unique_fields_names(names, localDeclList);
Expand Down Expand Up @@ -1938,19 +2017,8 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
else // CVC: This is case of "returns parameter <N>"
{
// Function modifies an argument whose value is the function return value.

if (udfReturnPos < 1 || ULONG(udfReturnPos) > parameters.getCount())
{
// CVC: We should devise new msg "position should be between 1 and #params";
// here it is: dsql_udf_return_pos_err

// External functions can not have more than 10 parameters
// Not strictly correct -- return position error
status_exception::raise(
Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
Arg::Gds(isc_dsql_command_err) << // gds__extern_func_err
Arg::Gds(isc_dsql_udf_return_pos_err) << Arg::Num(parameters.getCount()));
}
// It was checked in CreateAlterFunctionNode::dsqlPass
fb_assert(!(udfReturnPos < 1 || ULONG(udfReturnPos) > parameters.getCount()));

// We'll verify that SCALAR_ARRAY can't be used as a return type.
// The support for SCALAR_ARRAY is only for input parameters.
Expand Down
49 changes: 41 additions & 8 deletions src/dsql/ExprNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13115,21 +13115,54 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)

unsigned pos = 0;

for (auto& arg : node->args->items)
for (auto p_nodeArg = node->args->items.begin(), e_nodeArg = node->args->items.end();
p_nodeArg != e_nodeArg;
++p_nodeArg, ++pos)
{
if (pos < node->dsqlFunction->udf_arguments.getCount())
if (node->dsqlFunction->udf_arguments.getCount() <= pos)
{
PASS1_set_parameter_type(dsqlScratch, arg,
[&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos]; },
false);
// TODO: We should complain here in the future! The parameter is
// out of bounds or the function doesn't declare input params.

break;
}

auto& nodeArg = (*p_nodeArg);

if (!nodeArg)
{
continue;
}

const auto& udfArg = node->dsqlFunction->udf_arguments[pos];

PASS1_set_parameter_type(dsqlScratch, nodeArg,
[&] (dsc* desc) { *desc = udfArg.desc; },
false);

// We will provide a client with the additional information about
// the linked UDF argument of DSQL parameter with array.

if (udfArg.desc.dsc_dtype != dtype_array)
{
// It is not array and will be ignored.
}
else
if (!node->dsqlFunction->udf_name.package.isEmpty())
{
// We should complain here in the future! The parameter is
// out of bounds or the function doesn't declare input params.
// We can't provide with the exact information about package objects.
}
else
if (nodeArg->getType() == ExprNode::TYPE_PARAMETER)
Comment on lines +13146 to +13156
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, I will move these conditions into standalone method.

{
ParameterNode* const paramNode = nodeAs<ParameterNode>(nodeArg);
dsql_par* const parameter = paramNode->dsqlParameter;
parameter->par_rel_name = node->dsqlFunction->udf_name.identifier;
parameter->par_name = udfArg.name;

++pos;
fb_assert(!parameter->par_rel_name.isEmpty());
fb_assert(!parameter->par_name.isEmpty());
}
}

return node;
Expand Down
2 changes: 1 addition & 1 deletion src/dsql/StmtNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
if (!implemetingForward)
{
// ASF: dsqlFunction->udf_arguments is only checked for its count for now.
dsqlFunction->udf_arguments.add(dsc());
dsqlFunction->udf_arguments.add(dsql_udf_arg(pool, dsc(), nullptr));
}

if (param->defaultClause)
Expand Down
14 changes: 13 additions & 1 deletion src/dsql/dsql.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,18 @@ enum prc_flags_vals {
PRC_subproc = 4 // Sub procedure
};

//! User defined function argument
struct dsql_udf_arg
{
dsc desc;
MetaName name;

dsql_udf_arg(MemoryPool& p, const dsc& d, const char* n)
:desc(d), name(p, n)
{
}
};

//! User defined function block
class dsql_udf : public pool_alloc<dsql_type_udf>
{
Expand All @@ -375,7 +387,7 @@ class dsql_udf : public pool_alloc<dsql_type_udf>
//USHORT udf_character_length;
USHORT udf_flags;
QualifiedName udf_name;
Firebird::Array<dsc> udf_arguments;
Firebird::Array<dsql_udf_arg> udf_arguments;
bool udf_private; // Packaged private function
SSHORT udf_def_count; // number of inputs with default values
};
Expand Down
4 changes: 2 additions & 2 deletions src/dsql/metd.epp
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat
defaults++;
}

userFunc->udf_arguments.add(d);
userFunc->udf_arguments.add(dsql_udf_arg(dbb->dbb_pool, d, X.RDB$ARGUMENT_NAME));
}
}
END_FOR
Expand Down Expand Up @@ -875,7 +875,7 @@ dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat
defaults++;
}

userFunc->udf_arguments.add(d);
userFunc->udf_arguments.add(dsql_udf_arg(dbb->dbb_pool, d, X.RDB$ARGUMENT_NAME));
}
}
}
Expand Down