--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.steh...@gmail.com> wrote:
Hello
I did some cleaning on this feature, and I hope so I solve some Tom's
objections
Attached is a cleaned up version of the patch, the one linked from the
commit fest has some hunks failing to be applied to current CVS. This is
mainly for reporting and backup purposes, i'm proceeding looking into the
code deeper now. The patch passes make check and functionality check looks
fine.
Pavel, should I adjust the docs already (you've asked for volunteers...)?
If yes, have you something specific in mind?
--
Thanks
Bernd
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 3c8ce43..c6ece84 100644
*** a/doc/src/sgml/xfunc.sgml
--- b/doc/src/sgml/xfunc.sgml
*************** CREATE FUNCTION test(int, int) RETURNS i
*** 1101,1106 ****
--- 1101,1222 ----
</para>
</sect1>
+ <sect1 id="xfunc-notation">
+ <title>Positional, Mixed and Named notation</title>
+
+ <indexterm zone="xfunc-notation">
+ <primary>notation</primary>
+ <secondary>functions</secondary>
+ </indexterm>
+
+ <para>
+ Functions with named parameters should by called with <firstterm>mixed</firstterm>
+ or <firstterm>named notation</firstterm>. Any typical use expects <firstterm>posiotional
+ notation</firstterm>, when an order of parameters is important. For named notation
+ is important name of parameters. You can mix both notation - result is mixed notation.
+ Some first n parameters should be entered in positional parameters
+ and others in named notation. You don't need pass parameters than has
+ default value.
+ <programlisting>
+ CREATE FUNCTION fx(a int, b int, m int = 1, o int = 0) RETURNS int AS $$
+ SELECT ($1 + $2) * $3 + $4
+ $$ LANGUAGE SQL;
+ </programlisting>
+ Function <function>fx</function> has obligatory parameters: <literal>a</literal> and
+ <literal>b</literal> and optional parameters: <literal>m</literal> and <literal>o</literal>.
+ This function should be called with positional parameters. See <xref
+ linkend="xfunc-sql-parameter-defaults"> for a more detailed explanation of calling
+ function with default values.
+ </para>
+
+ <sect2 id="xfunc-notations-positional">
+ <title>Using positional notation</title>
+
+ <indexterm>
+ <primary>function</primary>
+ <secondary>positional notation</secondary>
+ </indexterm>
+
+ <para>
+ <screen>
+ SELECT fx(10,20);
+ fx
+ ----
+ 30
+ (1 row)
+
+ SELECT fx(10,20,2,50);
+ fx
+ -----
+ 110
+ (1 row)
+ </screen>
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-notations-named">
+ <title>Using named notation</title>
+
+ <indexterm>
+ <primary>function</primary>
+ <secondary>named notation</secondary>
+ </indexterm>
+
+ <para>
+ <screen>
+ SELECT fx(10 as a, 20 as b);
+ fx
+ ----
+ 30
+ (1 row)
+
+ SELECT fx(20 as b, 10 as a);
+ fx
+ ----
+ 30
+ (1 row)
+
+ SELECT fx(20 as b, 10 as a, 50 as o);
+ fx
+ ----
+ 80
+ (1 row)
+ </screen>
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-notations-mixed">
+ <title>Using mixed notation</title>
+
+ <indexterm>
+ <primary>function</primary>
+ <secondary>mixed notation</secondary>
+ </indexterm>
+
+ <para>
+ <screen>
+ SELECT fx(10,20, 50 as o);
+ fx
+ ----
+ 80
+ (1 row)
+
+ SELECT fx(10,20, 10 as m);
+ fx
+ -----
+ 300
+ (1 row)
+
+ SELECT fx(10,20, 50 as o, 2 as m);
+ fx
+ -----
+ 110
+ (1 row)
+ </screen>
+ </para>
+ </sect2>
+ </sect1>
+
<sect1 id="xfunc-volatility">
<title>Function Volatility Categories</title>
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 2b0cb35..714d337 100644
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 36,41 ****
--- 36,42 ----
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
+ #include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
*************** TypeIsVisible(Oid typid)
*** 603,610 ****
* The caller might end up discarding such an entry anyway, but if it selects
* such an entry it should react as though the call were ambiguous.
*/
FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
--- 604,723 ----
* The caller might end up discarding such an entry anyway, but if it selects
* such an entry it should react as though the call were ambiguous.
*/
+ static bool
+ VerifyCandidateNamedNotation(HeapTuple proctup, int pronargs, int nargs, List *argnames, short int pronargdefaults,
+ bool *use_defaults, short int **param_map)
+ {
+ Datum proargnames;
+ Oid *p_argtypes;
+ char **p_argnames;
+ char *p_argmodes;
+ bool isnull;
+ int pronallargs;
+ int i;
+ int pp; /* proargs position */
+ int ap; /* args position */
+ ListCell *lc;
+ FuncCallNotation used_notation = POSITIONAL_NOTATION; /* used only for asertion */
+
+ #define UNDEFINED_PARAMETER -1
+
+ Assert(argnames != NIL);
+
+ /* Ignore if not enough default expressions */
+ if (nargs + pronargdefaults < pronargs)
+ return false;
+
+ /* check proargnames */
+ proargnames = SysCacheGetAttr(PROCOID, proctup,
+ Anum_pg_proc_proargnames,
+ &isnull);
+ if (isnull)
+ return false;
+
+ pronallargs = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes);
+ Assert(p_argnames != NULL);
+
+ /*
+ * An number less or equal nargs means some arg,
+ * an number greather than nargs means some default.
+ */
+ *param_map = palloc(pronargs * sizeof(short int));
+ for (i = 0; i < pronargs; i++)
+ (*param_map)[i] = UNDEFINED_PARAMETER;
+
+ ap = 0;
+ foreach(lc, argnames)
+ {
+ char *argname = (char *) lfirst(lc);
+ bool found;
+
+ if (argname != NULL)
+ {
+ pp = 0;
+ found = false;
+ for (i = 0; i < pronallargs; i++)
+ {
+ /* skip all out params */
+ if (p_argmodes && (p_argmodes[i] != FUNC_PARAM_IN && p_argmodes[i] != FUNC_PARAM_INOUT))
+ continue;
+ if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
+ {
+ /* protect under twice entry same param via positional and named notation */
+ if ((*param_map)[pp] != UNDEFINED_PARAMETER)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("named parameter \"%s\" overlaps %d. positional parameter", argname, i + 1)));
+
+ found = true;
+ (*param_map)[pp] = ap; /* named parameter */
+ break;
+ }
+ /* increase only for IN and INOUT args */
+ pp++;
+ }
+ /* any name isn't in proargnames, fast leaving */
+ if (!found)
+ return false;
+
+ used_notation = NAMED_NOTATION;
+ }
+ else
+ {
+ Assert(used_notation == POSITIONAL_NOTATION);
+
+ (*param_map)[ap] = ap; /* positional parameter */
+ }
+ ap++;
+ }
+
+ Assert(used_notation == NAMED_NOTATION);
+
+ /* Have we check defaults? */
+ if (nargs < pronargs)
+ {
+ int first_arg_with_default = pronargs - pronargdefaults;
+
+ for (i = 0; i < pronargs; i++)
+ {
+ /* When we still missing param and no default is available, exit */
+ if ((*param_map)[i] == UNDEFINED_PARAMETER)
+ {
+ if (i < first_arg_with_default)
+ return false;
+ /* offset to defaults + nargs */
+ (*param_map)[i] = i - first_arg_with_default + nargs;
+ }
+ }
+ *use_defaults = true;
+ }
+
+ return true;
+ }
+
+
FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
*************** FuncnameGetCandidates(List *names, int n
*** 645,688 ****
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0;
! bool variadic;
! bool use_defaults;
! Oid va_elem_type;
FuncCandidateList newResult;
! /*
! * Check if function is variadic, and get variadic element type if so.
! * If expand_variadic is false, we should just ignore variadic-ness.
! */
! if (pronargs <= nargs && expand_variadic)
{
! va_elem_type = procform->provariadic;
! variadic = OidIsValid(va_elem_type);
! any_special |= variadic;
}
else
{
! va_elem_type = InvalidOid;
! variadic = false;
! }
! /*
! * Check if function can match by using parameter defaults.
! */
! if (pronargs > nargs && expand_defaults)
! {
! /* Ignore if not enough default expressions */
! if (nargs + procform->pronargdefaults < pronargs)
continue;
- use_defaults = true;
- any_special = true;
}
- else
- use_defaults = false;
-
- /* Ignore if it doesn't match requested argument count */
- if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
- continue;
if (OidIsValid(namespaceId))
{
--- 758,811 ----
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0;
! bool variadic = false;
! bool use_defaults = false;
! Oid va_elem_type = InvalidOid;
FuncCandidateList newResult;
+ short int *param_map = NULL;
! if (argnames != NIL)
{
! if (!VerifyCandidateNamedNotation(proctup, pronargs, nargs, argnames, procform->pronargdefaults,
! &use_defaults, ¶m_map))
! continue;
}
else
{
! /*
! * Check if function is variadic, and get variadic element type if so.
! * If expand_variadic is false, we should just ignore variadic-ness.
! */
! if (pronargs <= nargs && expand_variadic)
! {
! va_elem_type = procform->provariadic;
! variadic = OidIsValid(va_elem_type);
! any_special |= variadic;
! }
! else
! {
! va_elem_type = InvalidOid;
! variadic = false;
! }
! /*
! * Check if function can match by using parameter defaults.
! */
! if (pronargs > nargs && expand_defaults)
! {
! /* Ignore if not enough default expressions */
! if (nargs + procform->pronargdefaults < pronargs)
! continue;
! use_defaults = true;
! any_special = true;
! }
! else
! use_defaults = false;
!
! /* Ignore if it doesn't match requested argument count */
! if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
}
if (OidIsValid(namespaceId))
{
*************** FuncnameGetCandidates(List *names, int n
*** 722,727 ****
--- 845,851 ----
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
+ newResult->param_map = param_map;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
if (variadic)
*************** FuncnameGetCandidates(List *names, int n
*** 735,741 ****
}
else
newResult->nvargs = 0;
! newResult->ndargs = use_defaults ? pronargs - nargs : 0;
/*
* Does it have the same arguments as something we already accepted?
--- 859,872 ----
}
else
newResult->nvargs = 0;
! /* When named notation is used, then complete set of defaults is returned */
! if (argnames != NIL)
! {
! Assert(param_map != NULL);
! newResult->ndargs = procform->pronargdefaults;
! }
! else
! newResult->ndargs = use_defaults ? pronargs - nargs : 0;
/*
* Does it have the same arguments as something we already accepted?
*************** FunctionIsVisible(Oid funcid)
*** 932,938 ****
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, false, false);
for (; clist; clist = clist->next)
{
--- 1063,1069 ----
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
*************** FunctionIsVisible(Oid funcid)
*** 953,959 ****
/*
! * OpernameGetOprid
* Given a possibly-qualified operator name and exact input datatypes,
* look up the operator. Returns InvalidOid if not found.
*
--- 1084,1090 ----
/*
! * OpernameGetOpri
* Given a possibly-qualified operator name and exact input datatypes,
* look up the operator. Returns InvalidOid if not found.
*
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 845322e..26651d5 100644
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
*************** lookup_agg_function(List *fnName,
*** 321,329 ****
* function's return value. it also returns the true argument types to
* the function.
*/
! fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs,
! &true_oid_array, NULL);
/* only valid case is a normal function not returning a set */
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
--- 321,329 ----
* function's return value. it also returns the true argument types to
* the function.
*/
! fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs,
! &true_oid_array, NULL, NULL, NULL);
/* only valid case is a normal function not returning a set */
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1976648..89e506b 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyFuncExpr(FuncExpr *from)
*** 1013,1018 ****
--- 1013,1019 ----
COPY_SCALAR_FIELD(funcresulttype);
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat);
+ COPY_SCALAR_FIELD(argformat);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
*************** _copyFuncCall(FuncCall *from)
*** 1913,1918 ****
--- 1914,1930 ----
return newnode;
}
+ static ArgExpr *
+ _copyArgExpr(ArgExpr *from)
+ {
+ ArgExpr *newnode = makeNode(ArgExpr);
+
+ COPY_STRING_FIELD(name);
+ COPY_NODE_FIELD(expr);
+
+ return newnode;
+ }
+
static A_Star *
_copyAStar(A_Star *from)
{
*************** copyObject(void *from)
*** 4012,4017 ****
--- 4024,4032 ----
case T_FuncCall:
retval = _copyFuncCall(from);
break;
+ case T_ArgExpr:
+ retval = _copyArgExpr(from);
+ break;
case T_A_Star:
retval = _copyAStar(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b466f4..ee7f7f8 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalFuncExpr(FuncExpr *a, FuncExpr *b)
*** 235,240 ****
--- 235,241 ----
b->funcformat != COERCE_DONTCARE)
return false;
+ COMPARE_SCALAR_FIELD(argformat);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 9ed9018..de816b6 100644
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
*************** makeFuncExpr(Oid funcid, Oid rettype, Li
*** 342,347 ****
--- 342,348 ----
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcformat = fformat;
+ funcexpr->argformat = CONTINUOUS_LIST;
funcexpr->args = args;
funcexpr->location = -1;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6030292..9f178b7 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outFuncExpr(StringInfo str, FuncExpr *n
*** 871,876 ****
--- 871,877 ----
WRITE_OID_FIELD(funcresulttype);
WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
+ WRITE_ENUM_FIELD(argformat, ArgumentForm);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
}
*************** _outFuncCall(StringInfo str, FuncCall *n
*** 1795,1800 ****
--- 1796,1810 ----
}
static void
+ _outArgExpr(StringInfo str, ArgExpr *node)
+ {
+ WRITE_NODE_TYPE("ARGEXPR");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(expr);
+ }
+
+ static void
_outDefElem(StringInfo str, DefElem *node)
{
WRITE_NODE_TYPE("DEFELEM");
*************** _outNode(StringInfo str, void *obj)
*** 2765,2770 ****
--- 2775,2783 ----
case T_FuncCall:
_outFuncCall(str, obj);
break;
+ case T_ArgExpr:
+ _outArgExpr(str, obj);
+ break;
case T_DefElem:
_outDefElem(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4f7b740..71cca3e 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readFuncExpr(void)
*** 519,524 ****
--- 519,525 ----
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
+ READ_ENUM_FIELD(argformat, ArgumentForm);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index be75590..5dcabf4 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** static List *simplify_and_arguments(List
*** 95,105 ****
static Expr *simplify_boolean_equality(List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
! bool allow_inline,
eval_const_expressions_context *context);
! static List *add_function_defaults(List *args, Oid result_type,
! HeapTuple func_tuple,
! eval_const_expressions_context *context);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
--- 95,107 ----
static Expr *simplify_boolean_equality(List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
! bool leaky_list, bool allow_inline,
eval_const_expressions_context *context);
! static List *add_function_defaults(List *args,
! bool leaky_list,
! Oid result_type,
! HeapTuple func_tuple,
! eval_const_expressions_context *context);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
*************** eval_const_expressions_mutator(Node *nod
*** 2133,2139 ****
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
! &args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2135,2141 ----
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
! &args, expr->argformat == LEAKY_LIST,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2148,2153 ****
--- 2150,2156 ----
newexpr->funcresulttype = expr->funcresulttype;
newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat;
+ newexpr->argformat = expr->argformat;
newexpr->args = args;
newexpr->location = expr->location;
return (Node *) newexpr;
*************** eval_const_expressions_mutator(Node *nod
*** 2180,2186 ****
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2183,2189 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2271,2277 ****
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args,
false, context);
if (simple) /* successfully simplified it */
{
--- 2274,2280 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args, false,
false, context);
if (simple) /* successfully simplified it */
{
*************** eval_const_expressions_mutator(Node *nod
*** 2463,2469 ****
simple = simplify_function(outfunc,
CSTRINGOID, -1,
! &args,
true, context);
if (simple) /* successfully simplified output fn */
{
--- 2466,2472 ----
simple = simplify_function(outfunc,
CSTRINGOID, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified output fn */
{
*************** eval_const_expressions_mutator(Node *nod
*** 2481,2487 ****
simple = simplify_function(infunc,
expr->resulttype, -1,
! &args,
true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
--- 2484,2490 ----
simple = simplify_function(infunc,
expr->resulttype, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
*************** simplify_boolean_equality(List *args)
*** 3226,3231 ****
--- 3229,3235 ----
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List **args,
+ bool leaky_list,
bool allow_inline,
eval_const_expressions_context *context)
{
*************** simplify_function(Oid funcid, Oid result
*** 3247,3254 ****
elog(ERROR, "cache lookup failed for function %u", funcid);
/* While we have the tuple, check if we need to add defaults */
! if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
! *args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
func_tuple, context);
--- 3251,3258 ----
elog(ERROR, "cache lookup failed for function %u", funcid);
/* While we have the tuple, check if we need to add defaults */
! if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args) || leaky_list)
! *args = add_function_defaults(*args, leaky_list, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
func_tuple, context);
*************** simplify_function(Oid funcid, Oid result
*** 3271,3277 ****
* just like the parser did.
*/
static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
--- 3275,3281 ----
* just like the parser did.
*/
static List *
! add_function_defaults(List *args, bool leaky_list, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
*************** add_function_defaults(List *args, Oid re
*** 3297,3310 ****
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
! /* Delete any unused defaults from the list */
! ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
! if (ndelete < 0)
! elog(ERROR, "not enough default arguments");
! while (ndelete-- > 0)
! defaults = list_delete_first(defaults);
! /* And form the combined argument list */
! args = list_concat(args, defaults);
Assert(list_length(args) == funcform->pronargs);
/*
--- 3301,3341 ----
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
!
! if (leaky_list)
! {
! List *cargs = NIL; /* continuous argument list */
! ListCell *lc;
! int i = 0;
! bool first_default = funcform->pronargs - funcform->pronargdefaults;
!
! /* Replace gaps with elements from defaults */
! foreach(lc, args)
! {
! Node *arg = (Node *) lfirst(lc);
!
! if (arg == NULL)
! {
! Assert(i >= first_default);
! cargs = lappend(cargs, list_nth(defaults, i - first_default));
! }
! else
! cargs = lappend(cargs, arg);
! i++;
! }
! args = cargs;
! }
! else
! {
! /* Delete any unused defaults from the list */
! ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
! if (ndelete < 0)
! elog(ERROR, "not enough default arguments");
! while (ndelete-- > 0)
! defaults = list_delete_first(defaults);
! /* And form the combined argument list */
! args = list_concat(args, defaults);
! }
Assert(list_length(args) == funcform->pronargs);
/*
*************** evaluate_function(Oid funcid, Oid result
*** 3443,3448 ****
--- 3474,3480 ----
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
+ newexpr->funcformat = CONTINUOUS_LIST;
newexpr->args = args;
newexpr->location = -1;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 858e16c..ddfe95d 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static TypeName *TableFuncTypeName(List
*** 419,424 ****
--- 419,427 ----
%type <str> opt_existing_window_name
%type <ival> opt_frame_clause frame_extent frame_bound
+ %type <list> arg_expr_list
+ %type <node> arg_expr
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
*************** func_expr: func_name '(' ')' over_clause
*** 8794,8800 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8797,8803 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8818,8824 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8821,8827 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' arg_expr_list ',' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8830,8836 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8833,8839 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8846,8852 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8849,8855 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** expr_list: a_expr
*** 9568,9573 ****
--- 9571,9601 ----
$$ = lappend($1, $3);
}
;
+
+ /* Used for support of named notation.
+ */
+ arg_expr_list: arg_expr
+ {
+ $$ = list_make1($1);
+ }
+ | arg_expr_list ',' arg_expr
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+ arg_expr: a_expr
+ {
+ $$ = $1;
+ }
+ | a_expr AS param_name
+ {
+ ArgExpr *ae = makeNode(ArgExpr);
+ ae->expr = $1;
+ ae->name = $3;
+ $$ = (Node *) ae;
+ }
+ ;
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
*************** name_list: name
*** 9968,9974 ****
;
! name: ColId { $$ = $1; };
database_name:
ColId { $$ = $1; };
--- 9996,10002 ----
;
! name: ColId { $$ = $1; };
database_name:
ColId { $$ = $1; };
*************** func_name: type_function_name
*** 10001,10007 ****
/*
! * Constants
*/
AexprConst: Iconst
{
--- 10029,10035 ----
/*
! * Constants ToDo - PStehule
*/
AexprConst: Iconst
{
*************** AexprConst: Iconst
*** 10035,10044 ****
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
! | func_name '(' expr_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);
--- 10063,10080 ----
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
! | func_name '(' arg_expr_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
+ ListCell *lc;
+
+ /* Don't allow ArgExpr in this context */
+ foreach(lc, $3)
+ {
+ if (IsA((Node *) lfirst(lc),ArgExpr))
+ elog(ERROR, "don't use named parameters in this context");
+ }
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 428adbd..8ad5a2c 100644
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
*************** transformIndirection(ParseState *pstate,
*** 362,368 ****
list_make1(n),
list_make1(result),
false, false, false,
! NULL, true, -1);
}
}
/* process trailing subscripts, if any */
--- 362,368 ----
list_make1(n),
list_make1(result),
false, false, false,
! NULL, true, NIL, -1);
}
}
/* process trailing subscripts, if any */
*************** transformColumnRef(ParseState *pstate, C
*** 506,512 ****
list_make1(makeString(name2)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 506,512 ----
list_make1(makeString(name2)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformColumnRef(ParseState *pstate, C
*** 547,553 ****
list_make1(makeString(name3)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 547,553 ----
list_make1(makeString(name3)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformColumnRef(ParseState *pstate, C
*** 602,608 ****
list_make1(makeString(name4)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 602,608 ----
list_make1(makeString(name4)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformAExprIn(ParseState *pstate, A_E
*** 1091,1107 ****
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
! List *targs;
ListCell *args;
/* Transform the list of arguments ... */
- targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExpr(pstate,
! (Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
--- 1091,1158 ----
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
! List *targs = NIL;
ListCell *args;
+ List *argnames = NIL;
+ bool argnames_used = false;
+ FuncCallNotation notation = POSITIONAL_NOTATION;
/* Transform the list of arguments ... */
foreach(args, fn->args)
{
! char *name = NULL;
! Node *targ = NULL;
! Node *arg = lfirst(args);
!
! if (IsA(arg, ArgExpr))
! {
! ArgExpr *argexpr = (ArgExpr *) arg;
! ListCell *lc;
!
! Assert(argexpr->name != NULL);
!
! argnames_used = true;
! notation = NAMED_NOTATION;
!
! name = argexpr->name;
! targ = transformExpr(pstate, argexpr->expr);
!
! /* Check duplicates */
! for_each_cell(lc, lnext(args))
! {
! if (IsA(lfirst(lc), ArgExpr))
! {
! char *next_name = ((ArgExpr *) lfirst(lc))->name;
!
! Assert(next_name != NULL);
! if (strcmp(name, next_name) == 0)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("function parameter \"%s\" is used more then once", name),
! errhint("Check used names of next parameters."),
! parser_errposition(pstate, exprLocation(targ))));
! }
! }
! }
! else
! {
! targ = transformExpr(pstate, arg);
! if (notation != POSITIONAL_NOTATION)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("expected named expression"),
! errhint("You can't put positionals arguments behind the named arguments."),
! parser_errposition(pstate, exprLocation(targ))));
! }
!
! targs = lappend(targs, targ);
! argnames = lappend(argnames, name);
}
+ /* forgot list of NULLs */
+ if (!argnames_used)
+ argnames = NIL;
+
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
*************** transformFuncCall(ParseState *pstate, Fu
*** 1111,1116 ****
--- 1162,1168 ----
fn->func_variadic,
fn->over,
false,
+ argnames,
fn->location);
}
*************** transformCaseExpr(ParseState *pstate, Ca
*** 1167,1173 ****
Node *warg;
Assert(IsA(w, CaseWhen));
-
warg = (Node *) w->expr;
if (placeholder)
{
--- 1219,1224 ----
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 8ea76ab..556d4fe 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
*************** static void unknown_attribute(ParseState
*** 59,65 ****
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, int location)
{
Oid rettype;
Oid funcid;
--- 59,65 ----
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, List *argnames, int location)
{
Oid rettype;
Oid funcid;
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 75,80 ****
--- 75,83 ----
bool retset;
int nvargs;
FuncDetailCode fdresult;
+ short int *param_map;
+ int pronargs;
+ ArgumentForm argformat = CONTINUOUS_LIST;
/*
* Most of the rest of the parser just assumes that functions do not have
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 130,136 ****
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
! !func_variadic && list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
--- 133,139 ----
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
! !func_variadic && list_length(funcname) == 1 && argnames == NIL)
{
Oid argtype = actual_arg_types[0];
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 161,170 ****
* replaced by a suitable number of copies of its element type. We'll fix
* it up below. We may also have to deal with default arguments.
*/
! fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
! &declared_arg_types, &argdefaults);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
--- 164,173 ----
* replaced by a suitable number of copies of its element type. We'll fix
* it up below. We may also have to deal with default arguments.
*/
! fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
! &declared_arg_types, &argdefaults, ¶m_map, &pronargs);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 241,269 ****
parser_errposition(pstate, location)));
}
! /*
! * If there are default arguments, we have to include their types in
! * actual_arg_types for the purpose of checking generic type consistency.
! * However, we do NOT put them into the generated parse node, because
! * their actual values might change before the query gets run. The
! * planner has to insert the up-to-date values at plan time.
! */
! nargsplusdefs = nargs;
! foreach(l, argdefaults)
{
! Node *expr = (Node *) lfirst(l);
!
! /* probably shouldn't happen ... */
! if (nargsplusdefs >= FUNC_MAX_ARGS)
! ereport(ERROR,
! (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
! errmsg_plural("cannot pass more than %d argument to a function",
! "cannot pass more than %d arguments to a function",
! FUNC_MAX_ARGS,
! FUNC_MAX_ARGS),
! parser_errposition(pstate, location)));
!
! actual_arg_types[nargsplusdefs++] = exprType(expr);
}
/*
--- 244,305 ----
parser_errposition(pstate, location)));
}
!
! if (param_map != NULL)
{
! List *rfargs = NIL; /* reordered list of function arguments */
! int i;
!
! for (i = 0; i < pronargs; i++)
! {
! Node *expr = NULL;
!
! if (param_map[i] < nargs)
! {
! expr = (Node *) list_nth(fargs, param_map[i]);
! rfargs = lappend(rfargs, expr);
! /* when any arg goes out of narg */
! if (i >= nargs)
! argformat = LEAKY_LIST;
! }
! else
! {
! expr = (Node *) list_nth(argdefaults, param_map[i] - nargs);
! rfargs = lappend(rfargs, NULL);
! }
! actual_arg_types[i] = exprType(expr);
! }
!
! fargs = (argformat == LEAKY_LIST) ? rfargs : list_truncate(rfargs, nargs);
! nargsplusdefs = pronargs;
! }
! else
! {
! /*
! * If there are default arguments, we have to include their types in
! * actual_arg_types for the purpose of checking generic type consistency.
! * However, we do NOT put them into the generated parse node, because
! * their actual values might change before the query gets run. The
! * planner has to insert the up-to-date values at plan time.
! */
!
! nargsplusdefs = nargs;
! foreach(l, argdefaults)
! {
! Node *expr = (Node *) lfirst(l);
!
! /* probably shouldn't happen ... */
! if (nargsplusdefs >= FUNC_MAX_ARGS)
! ereport(ERROR,
! (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
! errmsg_plural("cannot pass more than %d argument to a function",
! "cannot pass more than %d arguments to a function",
! FUNC_MAX_ARGS,
! FUNC_MAX_ARGS),
! parser_errposition(pstate, location)));
!
! actual_arg_types[nargsplusdefs++] = exprType(expr);
! }
}
/*
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 319,324 ****
--- 355,361 ----
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+ funcexpr->argformat = argformat;
funcexpr->args = fargs;
funcexpr->location = location;
*************** func_select_candidate(int nargs,
*** 809,814 ****
--- 846,852 ----
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
+ List *argnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
*************** func_get_detail(List *funcname,
*** 817,824 ****
Oid *rettype, /* return value */
bool *retset, /* return value */
int *nvargs, /* return value */
! Oid **true_typeids, /* return value */
! List **argdefaults) /* optional return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
--- 855,864 ----
Oid *rettype, /* return value */
bool *retset, /* return value */
int *nvargs, /* return value */
! Oid **true_typeids, /* return value */
! List **argdefaults, /* optional return value */
! short int **param_map, /* optional return value */
! int *pronargs) /* optional return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
*************** func_get_detail(List *funcname,
*** 833,839 ****
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs,
expand_variadic, expand_defaults);
/*
--- 873,879 ----
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs, argnames,
expand_variadic, expand_defaults);
/*
*************** func_get_detail(List *funcname,
*** 987,992 ****
--- 1027,1040 ----
pform = (Form_pg_proc) GETSTRUCT(ftup);
*rettype = pform->prorettype;
*retset = pform->proretset;
+
+ if (param_map)
+ {
+ Assert(pronargs != NULL);
+ *pronargs = best_candidate->nargs;
+ *param_map = best_candidate->param_map;
+ }
+
/* fetch default args if caller wants 'em */
if (argdefaults)
{
*************** make_fn_arguments(ParseState *pstate,
*** 1060,1065 ****
--- 1108,1115 ----
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
+ Assert(lfirst(current_fargs) != NULL);
+
lfirst(current_fargs) = coerce_type(pstate,
lfirst(current_fargs),
actual_arg_types[i],
*************** LookupFuncName(List *funcname, int nargs
*** 1276,1282 ****
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, false, false);
while (clist)
{
--- 1326,1332 ----
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 0817e6a..dc58062 100644
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
*************** regprocin(PG_FUNCTION_ARGS)
*** 131,137 ****
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
! clist = FuncnameGetCandidates(names, -1, false, false);
if (clist == NULL)
ereport(ERROR,
--- 131,137 ----
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
! clist = FuncnameGetCandidates(names, -1, NIL, false, false);
if (clist == NULL)
ereport(ERROR,
*************** regprocout(PG_FUNCTION_ARGS)
*** 190,196 ****
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! -1, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
--- 190,196 ----
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! -1, NIL, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
*************** regprocedurein(PG_FUNCTION_ARGS)
*** 277,283 ****
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
! clist = FuncnameGetCandidates(names, nargs, false, false);
for (; clist; clist = clist->next)
{
--- 277,283 ----
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
! clist = FuncnameGetCandidates(names, nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 333cb25..d5877a5 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** generate_function_name(Oid funcid, int n
*** 6372,6380 ****
* specified argtypes.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
! NIL, nargs, argtypes, false, true,
&p_funcid, &p_rettype,
! &p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
p_result == FUNCDETAIL_AGGREGATE ||
p_result == FUNCDETAIL_WINDOWFUNC) &&
--- 6372,6380 ----
* specified argtypes.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
! NIL, NIL, nargs, argtypes, false, true,
&p_funcid, &p_rettype,
! &p_retset, &p_nvargs, &p_true_typeids, NULL, NULL, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
p_result == FUNCDETAIL_AGGREGATE ||
p_result == FUNCDETAIL_WINDOWFUNC) &&
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 956069d..a1cb70d 100644
*** a/src/include/catalog/namespace.h
--- b/src/include/catalog/namespace.h
*************** typedef struct _FuncCandidateList
*** 32,37 ****
--- 32,38 ----
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */
+ short int *param_map; /* maps external arguments to function arguments */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
*************** extern Oid TypenameGetTypid(const char *
*** 55,62 ****
extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
! bool expand_variadic,
! bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
--- 56,64 ----
extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
! List *argnames,
! bool expand_variadic,
! bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 925375b..ff7f799 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 376,381 ****
--- 376,382 ----
T_XmlSerialize,
T_WithClause,
T_CommonTableExpr,
+ T_ArgExpr,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9d53ab9..84e91e0 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct FuncCall
*** 282,287 ****
--- 282,305 ----
} FuncCall;
/*
+ * ArgExpr - an argument of function
+ */
+ typedef struct ArgExpr
+ {
+ NodeTag type;
+ char *name; /* an name of argument (when is specified) */
+ Node *expr; /* the argument */
+ } ArgExpr;
+
+
+ /* notation used for Func call params */
+ typedef enum FuncCallNotation
+ {
+ POSITIONAL_NOTATION,
+ NAMED_NOTATION
+ } FuncCallNotation;
+
+ /*
* A_Star - '*' representing all columns of a table or compound field
*
* This can appear within ColumnRef.fields, A_Indirection.indirection, and
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6e7f52b..a1bdf0d 100644
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef enum CoercionForm
*** 299,304 ****
--- 299,310 ----
COERCE_DONTCARE /* special case for planner */
} CoercionForm;
+ typedef enum ArgumentForm
+ {
+ CONTINUOUS_LIST, /* used for positional notation */
+ LEAKY_LIST /* used for named and mixed notation */
+ } ArgumentForm;
+
/*
* FuncExpr - expression node for a function call
*/
*************** typedef struct FuncExpr
*** 309,314 ****
--- 315,321 ----
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
CoercionForm funcformat; /* how to display this function call */
+ ArgumentForm argformat; /* what is format of argument list */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 2a49f00..940ed28 100644
*** a/src/include/parser/parse_func.h
--- b/src/include/parser/parse_func.h
*************** typedef enum
*** 45,58 ****
extern Node *ParseFuncOrColumn(ParseState *pstate,
List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, int location);
! extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
! int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid **true_typeids,
! List **argdefaults);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,
--- 45,58 ----
extern Node *ParseFuncOrColumn(ParseState *pstate,
List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, List *argnames, int location);
! extern FuncDetailCode func_get_detail(List *funcname, List *fargs, List *argnames,
! int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid **true_typeids,
! List **argdefaults, short int **param_map, int *pronargs);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 77f693c..9c60aaa 100644
*** a/src/test/regress/expected/polymorphism.out
--- b/src/test/regress/expected/polymorphism.out
*************** select dfunc('Hi');
*** 1038,1040 ****
--- 1038,1104 ----
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+ -- test function with named params and using named or mixed notation
+ -- fail, unnamed param behind named
+ create function dfunc(a int, b int = 1, c int) returns table (a int, b int, c int) as $$
+ select $1, $2, $3;
+ $$ language sql;
+ ERROR: input parameters after one with a default value must also have defaults
+ create function dfunc(a int, b int, c int=0, d int = 0) returns table (a int, b int, c int, d int) as $$
+ select $1, $2, $3, $4;
+ $$ language sql;
+ select (dfunc(10,20,30)).*;
+ a | b | c | d
+ ----+----+----+---
+ 10 | 20 | 30 | 0
+ (1 row)
+
+ select (dfunc(10 as a, 20 as b, 30 as c)).*;
+ a | b | c | d
+ ----+----+----+---
+ 10 | 20 | 30 | 0
+ (1 row)
+
+ select * from dfunc(10 as a, 20 as b);
+ a | b | c | d
+ ----+----+---+---
+ 10 | 20 | 0 | 0
+ (1 row)
+
+ select * from dfunc(10 as b, 20 as a);
+ a | b | c | d
+ ----+----+---+---
+ 20 | 10 | 0 | 0
+ (1 row)
+
+ select * from dfunc(0,0);
+ a | b | c | d
+ ---+---+---+---
+ 0 | 0 | 0 | 0
+ (1 row)
+
+ select * from dfunc(0,0,10 as c);
+ a | b | c | d
+ ---+---+----+---
+ 0 | 0 | 10 | 0
+ (1 row)
+
+ select * from dfunc(0,0,10 as d);
+ a | b | c | d
+ ---+---+---+----
+ 0 | 0 | 0 | 10
+ (1 row)
+
+ select * from dfunc(10 as x, 20 as b, 30 as c); --fail - unknown param
+ ERROR: function dfunc(integer, integer, integer) does not exist
+ LINE 1: select * from dfunc(10 as x, 20 as b, 30 as c);
+ ^
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+ select * from dfunc(10, 20 as b, 30); -- fail using positional notation begind named notation
+ ERROR: expected named expression
+ LINE 1: select * from dfunc(10, 20 as b, 30);
+ ^
+ HINT: You can't put positionals arguments behind the named arguments.
+ select * from dfunc(10,10,20 as a); --fail - a overlaps first positional parameter
+ ERROR: named parameter "a" overlaps 1. positional parameter
+ drop function dfunc(int, int, int, int);
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index c01871d..ff83723 100644
*** a/src/test/regress/sql/polymorphism.sql
--- b/src/test/regress/sql/polymorphism.sql
*************** select dfunc('Hi');
*** 624,626 ****
--- 624,650 ----
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+
+ -- test function with named params and using named or mixed notation
+ -- fail, unnamed param behind named
+ create function dfunc(a int, b int = 1, c int) returns table (a int, b int, c int) as $$
+ select $1, $2, $3;
+ $$ language sql;
+
+ create function dfunc(a int, b int, c int=0, d int = 0) returns table (a int, b int, c int, d int) as $$
+ select $1, $2, $3, $4;
+ $$ language sql;
+
+ select (dfunc(10,20,30)).*;
+ select (dfunc(10 as a, 20 as b, 30 as c)).*;
+ select * from dfunc(10 as a, 20 as b);
+ select * from dfunc(10 as b, 20 as a);
+ select * from dfunc(0,0);
+ select * from dfunc(0,0,10 as c);
+ select * from dfunc(0,0,10 as d);
+
+ select * from dfunc(10 as x, 20 as b, 30 as c); --fail - unknown param
+ select * from dfunc(10, 20 as b, 30); -- fail using positional notation begind named notation
+ select * from dfunc(10,10,20 as a); --fail - a overlaps first positional parameter
+
+ drop function dfunc(int, int, int, int);
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers