From 5dbd5a46ef7682c3aff0ea161171d674e5edbe4c Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 2 May 2023 21:58:44 -0300 Subject: [PATCH] Allow mixed (positional, named) arguments. --- doc/sql.extensions/README.named_arguments.md | 21 ++- src/dsql/ExprNodes.cpp | 158 ++++++++----------- src/dsql/StmtNodes.cpp | 114 +++++++------ src/dsql/parse.y | 11 +- src/dsql/pass1.cpp | 52 +++--- src/jrd/RecordSourceNodes.cpp | 28 ++-- src/jrd/cmp.cpp | 134 +++++++--------- 7 files changed, 244 insertions(+), 274 deletions(-) diff --git a/doc/sql.extensions/README.named_arguments.md b/doc/sql.extensions/README.named_arguments.md index bbcc27b119..404b208530 100644 --- a/doc/sql.extensions/README.named_arguments.md +++ b/doc/sql.extensions/README.named_arguments.md @@ -1,12 +1,15 @@ # 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. +Named arguments allows you to specify function and procedure arguments by their names, rather than only +by their positions. -It is especially useful when the routine have a lot of parameters and you want to specify them in arbitrary order or not specify some of them who have default values. +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. -It's currently not possible to mix positional and named arguments in the same call. +A call can use positional, named or mixed arguments. In mixed syntax, positional arguments must appear before +named arguments. ## Syntax @@ -23,8 +26,11 @@ It's currently not possible to mix positional and named arguments in the same ca [RETURNING_VALUES ...] ::= - - + | + [ {,} ] + + ::= + [ {, }... ] ::= [ {, }... ] @@ -40,6 +46,11 @@ select function_name(parameter2 => 'Two', parameter1 => 1) from rdb$database ``` +``` +select function_name(1, parameter2 => 'Two') + from rdb$database +``` + ``` execute procedure insert_customer( last_name => 'SCHUMACHER', diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index abb1f33896..8a9b42abf1 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -12976,7 +12976,7 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* predateCheck(node->function, "blr_invoke_function_type", "blr_invoke_function_args"); argCount = blrReader.getWord(); - node->args = PAR_args(tdbb, csb, argCount, (argNames ? argCount : MAX(argCount, node->function->fun_inputs))); + node->args = PAR_args(tdbb, csb, argCount, MAX(argCount, node->function->fun_inputs)); break; default: @@ -13013,6 +13013,14 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* node->args = PAR_args(tdbb, csb, argCount, node->function->fun_inputs); } + if (argNames && argNames->getCount() > argCount) + { + 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); @@ -13038,91 +13046,71 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* node->isSubRoutine = node->function->isSubRoutine(); Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_fun_param_mismatch) << name.toString(); - const auto mismatchInitialLength = mismatchStatus.length(); if (!node->args) node->args = FB_NEW_POOL(pool) ValueListNode(pool); - if (argNames && argNames->getCount() != node->args->items.getCount()) + const auto positionalArgCount = argNames ? argCount - argNames->getCount() : argCount; + auto argIt = node->args->items.begin(); + LeftPooledMap> argsByName; + + if (positionalArgCount) { - blrReader.setPos(argNamesPos); - PAR_error(csb, - Arg::Gds(isc_random) << "blr_invoke_function_arg_names count differs from blr_invoke_function_args"); + 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) { - LeftPooledMap> argsByName; - auto argIt = node->args->items.begin(); - for (const auto& argName : *argNames) { if (argsByName.put(argName, *argIt++)) mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << argName; } - - node->args->items.resize(node->function->getInputFields().getCount()); - argIt = node->args->items.begin(); - - for (auto& parameter : node->function->getInputFields()) - { - if (const auto argValue = argsByName.get(parameter->prm_name)) - { - *argIt = *argValue; - argsByName.remove(parameter->prm_name); - } - else - { - if (parameter->prm_default_value) - *argIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value); - else - mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name; - } - - ++argIt; - } - - if (argsByName.hasData()) - { - for (const auto& argPair : argsByName) - mismatchStatus << Arg::Gds(isc_param_not_exist) << argPair.first; - } } - else + + node->args->items.resize(node->function->getInputFields().getCount()); + argIt = node->args->items.begin(); + + for (auto& parameter : node->function->getInputFields()) { - // Check to see if the argument count matches. - - if (argCount > node->function->fun_inputs) - mismatchStatus << Arg::Gds(isc_wronumarg); - else if (argCount < node->function->fun_inputs - node->function->getDefaultCount()) + if (const auto argValue = argsByName.get(parameter->prm_name)) { - unsigned pos = 0; - - for (auto& parameter : node->function->getInputFields()) - { - if (++pos <= argCount) - continue; - - mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name; - - if (pos >= node->function->fun_inputs - node->function->getDefaultCount()) - break; - } + *argIt = *argValue; + argsByName.remove(parameter->prm_name); } else { - for (unsigned pos = argCount; pos < node->function->getInputFields().getCount(); ++pos) - { - auto parameter = node->function->getInputFields()[pos]; - fb_assert(parameter->prm_default_value); - node->args->items[pos] = CMP_clone_node(tdbb, csb, parameter->prm_default_value); - } + if (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 (mismatchStatus.length() > mismatchInitialLength) - status_exception::raise(mismatchStatus); + 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 (!node->function->isSubRoutine() && csb->collectingDependencies()) @@ -13589,20 +13577,31 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Arg::Gds(isc_random) << Arg::Str(name.toString())); } + auto argIt = node->args->items.begin(); + unsigned pos = 0; + + while (pos < node->args->items.getCount() - (node->dsqlArgNames ? node->dsqlArgNames->getCount() : 0)) + { + if (pos < node->dsqlFunction->udf_arguments.getCount()) + { + PASS1_set_parameter_type(dsqlScratch, *argIt++, + [&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos].desc; }, + false); + } + + ++pos; + } + if (node->dsqlArgNames) { - fb_assert(node->dsqlArgNames->getCount() == node->args->items.getCount()); + fb_assert(node->dsqlArgNames->getCount() <= node->args->items.getCount()); LeftPooledMap argsByName; for (const auto& arg : node->dsqlFunction->udf_arguments) argsByName.put(arg.name, &arg.desc); - bool mismatchError = false; Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_fun_param_mismatch) << Arg::Str(name.toString()); - - auto argIt = node->args->items.begin(); for (const auto& argName : *node->dsqlArgNames) { @@ -13613,32 +13612,13 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) false); } else - { - mismatchError = true; mismatchStatus << Arg::Gds(isc_param_not_exist) << argName; - } ++argIt; } - if (mismatchError) - status_exception::raise(mismatchStatus); - } - else - { - unsigned pos = 0; - - for (auto& arg : node->args->items) - { - if (pos < node->dsqlFunction->udf_arguments.getCount()) - { - PASS1_set_parameter_type(dsqlScratch, arg, - [&] (dsc* desc) { *desc = node->dsqlFunction->udf_arguments[pos].desc; }, - false); - } - - ++pos; - } + if (mismatchStatus.hasData()) + status_exception::raise(Arg::Gds(isc_fun_param_mismatch) << name.toString() << mismatchStatus); } return node; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 73ace62363..ace0998182 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -3003,7 +3003,7 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr predateCheck(node->procedure, "blr_invsel_procedure_type", "blr_invsel_procedure_in_args"); inArgCount = blrReader.getWord(); node->inputSources = PAR_args(tdbb, csb, inArgCount, - (inArgNames ? inArgCount : MAX(inArgCount, node->procedure->getInputFields().getCount()))); + MAX(inArgCount, node->procedure->getInputFields().getCount())); break; case blr_invsel_procedure_out_arg_names: @@ -3071,6 +3071,14 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr break; } + 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 (!node->procedure) { blrReader.setPos(blrStartPos); @@ -3092,14 +3100,6 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr if (!node->outputTargets) node->outputTargets = FB_NEW_POOL(pool) ValueListNode(pool); - if (inArgNames && inArgNames->getCount() != node->inputSources->items.getCount()) - { - blrReader.setPos(inArgNamesPos); - PAR_error(csb, - Arg::Gds(isc_random) << - "blr_invsel_procedure_in_arg_names count differs from blr_invsel_procedure_in_args"); - } - if (outArgNames && outArgNames->getCount() != node->outputTargets->items.getCount()) { blrReader.setPos(outArgNamesPos); @@ -3125,13 +3125,9 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr } } - Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_prcmismat) << node->procedure->getName().toString(); - const auto mismatchInitialLength = mismatchStatus.length(); - node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount()); - mismatchStatus << CMP_procedure_arguments( + Arg::StatusVector mismatchStatus = CMP_procedure_arguments( tdbb, csb, node->procedure, @@ -3153,8 +3149,8 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr node->outputSources, node->outputMessage); - if (mismatchStatus.length() > mismatchInitialLength) - status_exception::raise(mismatchStatus); + if (mismatchStatus.hasData()) + status_exception::raise(Arg::Gds(isc_prcmismat) << node->procedure->getName().toString() << mismatchStatus); if (csb->collectingDependencies() && !node->procedure->isSubRoutine()) { @@ -3246,63 +3242,59 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // Handle input parameters. - if (node->dsqlInputArgNames) + if (inputSources && inputSources->items.hasData()) { - fb_assert(node->dsqlInputArgNames->getCount() == node->inputSources->items.getCount()); + 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; - LeftPooledMap argsByName; - - for (const auto* field = procedure->prc_inputs; field; field = field->fld_next) - argsByName.put(field->fld_name, field); - - bool mismatchError = false; - Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_prcmismat) << dsqlName.toString(); - - auto argIt = node->inputSources->items.begin(); - - for (const auto& argName : *node->dsqlInputArgNames) + while (pos < positionalArgCount && field && sourceArgIt != sourceArgEnd) { - if (const auto field = argsByName.get(argName)) - { - dsc descNode; - DsqlDescMaker::fromField(&descNode, *field); + dsc descNode; + DsqlDescMaker::fromField(&descNode, field); - PASS1_set_parameter_type(dsqlScratch, *argIt, - [&] (dsc* desc) { *desc = descNode; }, - false); - } - else - { - mismatchError = true; - mismatchStatus << Arg::Gds(isc_param_not_exist) << argName; - } + PASS1_set_parameter_type(dsqlScratch, *sourceArgIt, + [&] (dsc* desc) { *desc = descNode; }, + false); - ++argIt; + field = field->fld_next; + ++pos; + ++sourceArgIt; } - if (mismatchError) - status_exception::raise(mismatchStatus); - } - else - { - if (inputSources && inputSources->items.hasData()) + if (node->dsqlInputArgNames) { - // Initialize this stack variable, and make it look like a node. - dsc descNode; + fb_assert(node->dsqlInputArgNames->getCount() <= node->inputSources->items.getCount()); - auto ptr = node->inputSources->items.begin(); - const auto end = node->inputSources->items.end(); + LeftPooledMap argsByName; - for (const dsql_fld* field = procedure->prc_inputs; field && ptr != end; ++ptr, field = field->fld_next) + for (const auto* field = procedure->prc_inputs; field; field = field->fld_next) + argsByName.put(field->fld_name, field); + + Arg::StatusVector mismatchStatus; + + for (const auto& argName : *node->dsqlInputArgNames) { - DEV_BLKCHK(field, dsql_type_fld); - DEV_BLKCHK(*ptr, dsql_type_nod); - DsqlDescMaker::fromField(&descNode, field); - PASS1_set_parameter_type(dsqlScratch, *ptr, - [&] (dsc* desc) { *desc = descNode; }, - false); + 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); } } diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 8bc45d0f38..bf7b8aeaa7 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -8618,12 +8618,21 @@ argument_list_opt %type argument_list argument_list : named_argument_list - { $$ = $1; } | value_list { $$ = newNode*, ValueListNode*>>(); $$->second = $1; } + | value_list ',' named_argument_list + { + $$ = $3; + + for (auto item : $$->second->items) + $1->add(item); + + delete $$->second; + $$->second = $1; + } ; %type named_argument_list diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 18418aaa18..ac7812eb4d 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -544,21 +544,38 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* if (count) { auto inputList = context->ctx_proc_inputs; + auto argIt = inputList->items.begin(); + const auto argEnd = inputList->items.end(); + + 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) + { + 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()); + fb_assert(procNode->dsqlInputArgNames->getCount() <= inputList->items.getCount()); LeftPooledMap argsByName; for (const auto* field = procedure->prc_inputs; field; field = field->fld_next) argsByName.put(field->fld_name, field); - bool mismatchError = false; Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_prcmismat) << procNode->dsqlName.toString(); - - auto argIt = inputList->items.begin(); for (const auto& argName : *procNode->dsqlInputArgNames) { @@ -572,34 +589,13 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* false); } else - { - mismatchError = true; mismatchStatus << Arg::Gds(isc_param_not_exist) << argName; - } ++argIt; } - if (mismatchError) - status_exception::raise(mismatchStatus); - } - else - { - // Initialize this stack variable, and make it look like a node - dsc descNode; - - auto ptr = inputList->items.begin(); - const auto end = inputList->items.end(); - - for (const dsql_fld* field = procedure->prc_inputs; ptr != end; ++ptr, field = field->fld_next) - { - DEV_BLKCHK(field, dsql_type_fld); - DEV_BLKCHK(*ptr, dsql_type_nod); - DsqlDescMaker::fromField(&descNode, field); - PASS1_set_parameter_type(dsqlScratch, *ptr, - [&] (dsc* desc) { *desc = descNode; }, - false); - } + if (mismatchStatus.hasData()) + status_exception::raise(Arg::Gds(isc_prcmismat) << procNode->dsqlName.toString() << mismatchStatus); } } } diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 4e5d010671..0a6a0ba511 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -987,7 +987,7 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch inArgCount = blrReader.getWord(); node->inputSources = PAR_args(tdbb, csb, inArgCount, - (inArgNames ? inArgCount : MAX(inArgCount, node->procedure->getInputFields().getCount()))); + MAX(inArgCount, node->procedure->getInputFields().getCount())); break; case blr_invsel_procedure_context: @@ -1069,6 +1069,14 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch fb_assert(false); } + 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 (!node->procedure) { blrReader.setPos(blrStartPos); @@ -1119,21 +1127,9 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch if (!node->inputSources) node->inputSources = FB_NEW_POOL(pool) ValueListNode(pool); - if (inArgNames && inArgNames->getCount() != node->inputSources->items.getCount()) - { - blrReader.setPos(inArgNamesPos); - PAR_error(csb, - Arg::Gds(isc_random) << - "blr_invsel_procedure_in_arg_names count differs from blr_invsel_procedure_in_args"); - } - - Arg::StatusVector mismatchStatus; - mismatchStatus << Arg::Gds(isc_prcmismat) << node->procedure->getName().toString(); - const auto mismatchInitialLength = mismatchStatus.length(); - node->inputTargets = FB_NEW_POOL(pool) ValueListNode(pool, node->procedure->getInputFields().getCount()); - mismatchStatus << CMP_procedure_arguments( + Arg::StatusVector mismatchStatus= CMP_procedure_arguments( tdbb, csb, node->procedure, @@ -1144,8 +1140,8 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch node->inputTargets, node->inputMessage); - if (mismatchStatus.length() > mismatchInitialLength) - status_exception::raise(mismatchStatus); + if (mismatchStatus.hasData()) + status_exception::raise(Arg::Gds(isc_prcmismat) << node->procedure->getName().toString() << mismatchStatus); if (csb->collectingDependencies() && !node->procedure->isSubRoutine()) { diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 98b6060fd8..7dde2a8927 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -484,9 +484,6 @@ Arg::StatusVector CMP_procedure_arguments( else targets = FB_NEW_POOL(pool) ValueListNode(pool, argCount); - auto sourceArgIt = sources->items.begin(); - auto targetArgIt = targets->items.begin(); - // We have a few parameters. Get on with creating the message block // Outer messages map may start with 2, but they are always in the routine start. USHORT n = ++csb->csb_msg_number; @@ -519,88 +516,77 @@ Arg::StatusVector CMP_procedure_arguments( message->format = fmtCopy; // --- end of fix --- + const auto positionalArgCount = argNames ? argCount - argNames->getCount() : argCount; + auto sourceArgIt = sources->items.begin(); + LeftPooledMap> 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) { - LeftPooledMap> argsByName; - for (const auto& argName : *argNames) { if (argsByName.put(argName, *sourceArgIt++)) mismatchStatus << Arg::Gds(isc_param_multiple_assignments) << argName; } - - sourceArgIt = sources->items.begin(); - - for (auto& parameter : fields) - { - if (const auto argValue = argsByName.get(parameter->prm_name)) - { - *sourceArgIt = *argValue; - argsByName.remove(parameter->prm_name); - } - else if (isInput) - { - if (parameter->prm_default_value) - *sourceArgIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value); - else - mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name; - } - - ++sourceArgIt; - - const auto paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); - paramNode->messageNumber = message->messageNumber; - paramNode->message = message; - paramNode->argNumber = parameter->prm_number * 2; - - const auto paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); - paramFlagNode->messageNumber = message->messageNumber; - paramFlagNode->message = message; - paramFlagNode->argNumber = parameter->prm_number * 2 + 1; - - paramNode->argFlag = paramFlagNode; - - *targetArgIt++ = paramNode; - } - - if (argsByName.hasData()) - { - for (const auto& argPair : argsByName) - mismatchStatus << Arg::Gds(isc_param_not_exist) << argPair.first; - } } - else + + sourceArgIt = sources->items.begin(); + auto targetArgIt = targets->items.begin(); + + for (auto& parameter : fields) { - for (unsigned i = 0; i < (isInput ? fields.getCount() : argCount); ++i) + if (const auto argValue = argsByName.get(parameter->prm_name)) { - if (isInput) - { - // default value for parameter - if (i >= argCount) - { - auto parameter = fields[i]; - - if (parameter->prm_default_value) - *sourceArgIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value); - } - - ++sourceArgIt; - } - - const auto paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); - paramNode->messageNumber = message->messageNumber; - paramNode->message = message; - paramNode->argNumber = i * 2; - - const auto paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); - paramFlagNode->messageNumber = message->messageNumber; - paramFlagNode->message = message; - paramFlagNode->argNumber = i * 2 + 1; - - paramNode->argFlag = paramFlagNode; - - *targetArgIt++ = paramNode; + *sourceArgIt = *argValue; + argsByName.remove(parameter->prm_name); } + else if (isInput) + { + if (parameter->prm_default_value) + *sourceArgIt = CMP_clone_node(tdbb, csb, parameter->prm_default_value); + else + mismatchStatus << Arg::Gds(isc_param_no_default_not_specified) << parameter->prm_name; + } + + ++sourceArgIt; + + const auto paramNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); + paramNode->messageNumber = message->messageNumber; + paramNode->message = message; + paramNode->argNumber = parameter->prm_number * 2; + + const auto paramFlagNode = FB_NEW_POOL(csb->csb_pool) ParameterNode(csb->csb_pool); + paramFlagNode->messageNumber = message->messageNumber; + paramFlagNode->message = message; + paramFlagNode->argNumber = parameter->prm_number * 2 + 1; + + paramNode->argFlag = paramFlagNode; + + *targetArgIt++ = paramNode; + } + + if (argsByName.hasData()) + { + for (const auto& argPair : argsByName) + mismatchStatus << Arg::Gds(isc_param_not_exist) << argPair.first; } }