mixed, named notation support

Started by Pavel Stehulealmost 17 years ago49 messages
#1Pavel Stehule
pavel.stehule@gmail.com
1 attachment(s)

Hello

I did some cleaning on this feature, and I hope so I solve some Tom's objections

features:
* PostgreSQL's specific syntax for named parameter: value AS name,
* Doesn't change rules for defaults,
* Get defaults for named, mixed notation in planner time.

ToDo: enhance documentation (any volunteer?)

regards
Pavel Stehule

CREATE FUNCTION fx(a int, b int, m int = 1, o int = 0) RETURNS int AS $$
SELECT ($1 + $2) * $3 + $4
$$ LANGUAGE SQL;

-- positional notation
SELECT fx(10,20);
fx
----
30
(1 row)

SELECT fx(10,20,2,50);
fx
-----
110
(1 row)

-- named notation
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)

-- mixed notation
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)

Attachments:

named_notation_20090305.01.difftext/x-patch; charset=US-ASCII; name=named_notation_20090305.01.diffDownload
*** ./doc/src/sgml/xfunc.sgml.orig	2009-03-05 07:37:10.000000000 +0100
--- ./doc/src/sgml/xfunc.sgml	2009-03-05 08:30:48.000000000 +0100
***************
*** 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>
  
*** ./src/backend/catalog/namespace.c.orig	2009-03-03 14:48:25.000000000 +0100
--- ./src/backend/catalog/namespace.c	2009-03-04 18:31:20.000000000 +0100
***************
*** 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"
***************
*** 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;
***************
*** 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, &param_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))
  		{
***************
*** 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)
***************
*** 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?
***************
*** 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)
  		{
***************
*** 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.
   *
*** ./src/backend/catalog/pg_aggregate.c.orig	2009-03-03 16:44:18.000000000 +0100
--- ./src/backend/catalog/pg_aggregate.c	2009-03-04 14:15:08.000000000 +0100
***************
*** 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))
*** ./src/backend/nodes/copyfuncs.c.orig	2009-03-03 13:11:22.000000000 +0100
--- ./src/backend/nodes/copyfuncs.c	2009-03-04 14:25:54.000000000 +0100
***************
*** 1009,1014 ****
--- 1009,1015 ----
  	COPY_SCALAR_FIELD(funcresulttype);
  	COPY_SCALAR_FIELD(funcretset);
  	COPY_SCALAR_FIELD(funcformat);
+ 	COPY_SCALAR_FIELD(argformat);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
  
***************
*** 1906,1911 ****
--- 1907,1923 ----
  	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)
  {
***************
*** 4026,4031 ****
--- 4038,4046 ----
  		case T_FuncCall:
  			retval = _copyFuncCall(from);
  			break;
+ 		case T_ArgExpr:
+ 			retval = _copyArgExpr(from);
+ 			break;
  		case T_A_Star:
  			retval = _copyAStar(from);
  			break;
*** ./src/backend/nodes/equalfuncs.c.orig	2009-03-04 14:28:58.000000000 +0100
--- ./src/backend/nodes/equalfuncs.c	2009-03-04 14:31:26.000000000 +0100
***************
*** 235,240 ****
--- 235,241 ----
  		b->funcformat != COERCE_DONTCARE)
  		return false;
  
+ 	COMPARE_SCALAR_FIELD(argformat);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
  
*** ./src/backend/nodes/makefuncs.c.orig	2009-03-04 14:26:40.000000000 +0100
--- ./src/backend/nodes/makefuncs.c	2009-03-04 14:27:54.000000000 +0100
***************
*** 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;
  
*** ./src/backend/nodes/outfuncs.c.orig	2009-03-03 13:11:23.000000000 +0100
--- ./src/backend/nodes/outfuncs.c	2009-03-04 14:31:06.000000000 +0100
***************
*** 866,871 ****
--- 866,872 ----
  	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);
  }
***************
*** 1786,1791 ****
--- 1787,1801 ----
  }
  
  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");
***************
*** 2764,2769 ****
--- 2774,2782 ----
  			case T_FuncCall:
  				_outFuncCall(str, obj);
  				break;
+ 			case T_ArgExpr:
+ 				_outArgExpr(str, obj);
+ 				break;
  			case T_DefElem:
  				_outDefElem(str, obj);
  				break;
*** ./src/backend/nodes/readfuncs.c.orig	2009-03-04 14:29:23.000000000 +0100
--- ./src/backend/nodes/readfuncs.c	2009-03-04 14:32:00.000000000 +0100
***************
*** 518,523 ****
--- 518,524 ----
  	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);
  
*** ./src/backend/optimizer/util/clauses.c.orig	2009-03-04 14:21:48.000000000 +0100
--- ./src/backend/optimizer/util/clauses.c	2009-03-04 17:58:19.000000000 +0100
***************
*** 94,103 ****
  					   bool *haveNull, bool *forceFalse);
  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,
--- 94,104 ----
  					   bool *haveNull, bool *forceFalse);
  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,
***************
*** 2132,2138 ****
  		 */
  		simple = simplify_function(expr->funcid,
  								   expr->funcresulttype, exprTypmod(node),
! 								   &args,
  								   true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
--- 2133,2139 ----
  		 */
  		simple = simplify_function(expr->funcid,
  								   expr->funcresulttype, exprTypmod(node),
! 								   &args, expr->argformat == LEAKY_LIST,
  								   true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
***************
*** 2147,2152 ****
--- 2148,2154 ----
  		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;
***************
*** 2179,2185 ****
  		 */
  		simple = simplify_function(expr->opfuncid,
  								   expr->opresulttype, -1,
! 								   &args,
  								   true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
--- 2181,2187 ----
  		 */
  		simple = simplify_function(expr->opfuncid,
  								   expr->opresulttype, -1,
! 								   &args, false,
  								   true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
***************
*** 2270,2276 ****
  			 */
  			simple = simplify_function(expr->opfuncid,
  									   expr->opresulttype, -1,
! 									   &args,
  									   false, context);
  			if (simple)			/* successfully simplified it */
  			{
--- 2272,2278 ----
  			 */
  			simple = simplify_function(expr->opfuncid,
  									   expr->opresulttype, -1,
! 									   &args, false,
  									   false, context);
  			if (simple)			/* successfully simplified it */
  			{
***************
*** 2462,2468 ****
  
  		simple = simplify_function(outfunc,
  								   CSTRINGOID, -1,
! 								   &args,
  								   true, context);
  		if (simple)				/* successfully simplified output fn */
  		{
--- 2464,2470 ----
  
  		simple = simplify_function(outfunc,
  								   CSTRINGOID, -1,
! 								   &args, false,
  								   true, context);
  		if (simple)				/* successfully simplified output fn */
  		{
***************
*** 2480,2486 ****
  
  			simple = simplify_function(infunc,
  									   expr->resulttype, -1,
! 									   &args,
  									   true, context);
  			if (simple)			/* successfully simplified input fn */
  				return (Node *) simple;
--- 2482,2488 ----
  
  			simple = simplify_function(infunc,
  									   expr->resulttype, -1,
! 									   &args, false,
  									   true, context);
  			if (simple)			/* successfully simplified input fn */
  				return (Node *) simple;
***************
*** 3225,3230 ****
--- 3227,3233 ----
  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)
  {
***************
*** 3246,3253 ****
  		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);
--- 3249,3256 ----
  		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);
***************
*** 3270,3276 ****
   * 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);
--- 3273,3279 ----
   * 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);
***************
*** 3296,3309 ****
  	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);
  
  	/*
--- 3299,3339 ----
  	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);
  
  	/*
***************
*** 3442,3447 ****
--- 3472,3478 ----
  	newexpr->funcresulttype = result_type;
  	newexpr->funcretset = false;
  	newexpr->funcformat = COERCE_DONTCARE;		/* doesn't matter */
+ 	newexpr->funcformat = CONTINUOUS_LIST;
  	newexpr->args = args;
  	newexpr->location = -1;
  
*** ./src/backend/parser/gram.y.orig	2009-02-24 11:06:33.000000000 +0100
--- ./src/backend/parser/gram.y	2009-03-03 13:08:21.000000000 +0100
***************
*** 420,425 ****
--- 420,428 ----
  %type <str>		opt_existing_window_name
  %type <ival>	opt_frame_clause frame_extent frame_bound
  
+ %type <list>	arg_expr_list
+ %type <node>	arg_expr
+ 
  
  /*
   * If you make any token changes, update the keyword table in
***************
*** 8776,8782 ****
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
--- 8779,8785 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' arg_expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
***************
*** 8800,8806 ****
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
--- 8803,8809 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' arg_expr_list ',' VARIADIC a_expr ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
***************
*** 8812,8818 ****
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' ALL expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
--- 8815,8821 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' ALL arg_expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
***************
*** 8828,8834 ****
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' DISTINCT expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
--- 8831,8837 ----
  					n->location = @1;
  					$$ = (Node *)n;
  				}
! 			| func_name '(' DISTINCT arg_expr_list ')' over_clause
  				{
  					FuncCall *n = makeNode(FuncCall);
  					n->funcname = $1;
***************
*** 9550,9555 ****
--- 9553,9583 ----
  					$$ = 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); }
***************
*** 9950,9956 ****
  		;
  
  
! name:		ColId									{ $$ = $1; };
  
  database_name:
  			ColId									{ $$ = $1; };
--- 9978,9984 ----
  		;
  
  
! name:		ColId								{ $$ = $1; };
  
  database_name:
  			ColId									{ $$ = $1; };
***************
*** 9980,9986 ****
  
  
  /*
!  * Constants
   */
  AexprConst: Iconst
  				{
--- 10008,10014 ----
  
  
  /*
!  * Constants ToDo - PStehule
   */
  AexprConst: Iconst
  				{
***************
*** 10014,10023 ****
  					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);
--- 10042,10059 ----
  					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);
*** ./src/backend/parser/parse_expr.c.orig	2009-03-03 12:56:38.000000000 +0100
--- ./src/backend/parser/parse_expr.c	2009-03-03 14:18:12.000000000 +0100
***************
*** 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 */
***************
*** 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;
  			}
***************
*** 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;
  			}
***************
*** 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;
  			}
***************
*** 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,
***************
*** 1111,1116 ****
--- 1162,1168 ----
  							 fn->func_variadic,
  							 fn->over,
  							 false,
+ 							 argnames,
  							 fn->location);
  }
  
***************
*** 1167,1173 ****
  		Node	   *warg;
  
  		Assert(IsA(w, CaseWhen));
- 
  		warg = (Node *) w->expr;
  		if (placeholder)
  		{
--- 1219,1224 ----
*** ./src/backend/parser/parse_func.c.orig	2009-03-03 12:56:45.000000000 +0100
--- ./src/backend/parser/parse_func.c	2009-03-04 15:46:52.000000000 +0100
***************
*** 63,69 ****
  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;
--- 63,69 ----
  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;
***************
*** 79,84 ****
--- 79,87 ----
  	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
***************
*** 132,138 ****
  	 * 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];
  
--- 135,141 ----
  	 * 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];
  
***************
*** 163,172 ****
  	 * 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)
  	{
  		/*
--- 166,175 ----
  	 * 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, &param_map, &pronargs);
  	if (fdresult == FUNCDETAIL_COERCION)
  	{
  		/*
***************
*** 243,248 ****
--- 246,281 ----
  					 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.
***************
*** 250,269 ****
  	 * 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("cannot pass more than %d arguments to a function",
! 							FUNC_MAX_ARGS),
! 					 parser_errposition(pstate, location)));
  
! 		actual_arg_types[nargsplusdefs++] = exprType(expr);
  	}
  
  	/*
--- 283,304 ----
  	 * 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("cannot pass more than %d arguments to a function",
! 								FUNC_MAX_ARGS),
! 						 parser_errposition(pstate, location)));
  
! 			actual_arg_types[nargsplusdefs++] = exprType(expr);
! 		}
  	}
  
  	/*
***************
*** 319,324 ****
--- 354,360 ----
  		funcexpr->funcresulttype = rettype;
  		funcexpr->funcretset = retset;
  		funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+ 		funcexpr->argformat = argformat;
  		funcexpr->args = fargs;
  		funcexpr->location = location;
  
***************
*** 807,812 ****
--- 843,849 ----
  FuncDetailCode
  func_get_detail(List *funcname,
  				List *fargs,
+ 				List *argnames,
  				int nargs,
  				Oid *argtypes,
  				bool expand_variadic,
***************
*** 816,828 ****
  				bool *retset,	/* return value */
  				int *nvargs,	/* return value */
  				Oid **true_typeids,		/* return value */
! 				List **argdefaults)		/* optional return value */
  {
  	FuncCandidateList raw_candidates;
  	FuncCandidateList best_candidate;
  
  	/* Get list of possible candidates from namespace search */
! 	raw_candidates = FuncnameGetCandidates(funcname, nargs,
  										   expand_variadic, expand_defaults);
  
  	/*
--- 853,867 ----
  				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;
  
  	/* Get list of possible candidates from namespace search */
! 	raw_candidates = FuncnameGetCandidates(funcname, nargs, argnames,
  										   expand_variadic, expand_defaults);
  
  	/*
***************
*** 976,981 ****
--- 1015,1028 ----
  		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)
  		{
***************
*** 1141,1146 ****
--- 1188,1195 ----
  		/* 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],
***************
*** 1357,1363 ****
  {
  	FuncCandidateList clist;
  
! 	clist = FuncnameGetCandidates(funcname, nargs, false, false);
  
  	while (clist)
  	{
--- 1406,1412 ----
  {
  	FuncCandidateList clist;
  
! 	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
  
  	while (clist)
  	{
*** ./src/backend/utils/adt/regproc.c.orig	2009-03-03 14:56:09.000000000 +0100
--- ./src/backend/utils/adt/regproc.c	2009-03-03 14:57:02.000000000 +0100
***************
*** 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,
***************
*** 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;
***************
*** 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)
  	{
*** ./src/backend/utils/adt/ruleutils.c.orig	2009-03-03 14:56:02.000000000 +0100
--- ./src/backend/utils/adt/ruleutils.c	2009-03-04 14:17:33.000000000 +0100
***************
*** 6322,6330 ****
  	 * 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) &&
--- 6322,6330 ----
  	 * 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) &&
*** ./src/include/catalog/namespace.h.orig	2009-03-03 14:49:23.000000000 +0100
--- ./src/include/catalog/namespace.h	2009-03-04 09:14:42.000000000 +0100
***************
*** 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 */
  
***************
*** 54,60 ****
  extern Oid	TypenameGetTypid(const char *typname);
  extern bool TypeIsVisible(Oid typid);
  
! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
  											   bool expand_variadic,
  											   bool expand_defaults);
  extern bool FunctionIsVisible(Oid funcid);
--- 55,61 ----
  extern Oid	TypenameGetTypid(const char *typname);
  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);
*** ./src/include/nodes/nodes.h.orig	2009-02-25 04:30:37.000000000 +0100
--- ./src/include/nodes/nodes.h	2009-03-03 13:13:42.000000000 +0100
***************
*** 378,383 ****
--- 378,384 ----
  	T_XmlSerialize,
  	T_WithClause,
  	T_CommonTableExpr,
+ 	T_ArgExpr,
  
  	/*
  	 * TAGS FOR RANDOM OTHER STUFF
*** ./src/include/nodes/parsenodes.h.orig	2009-03-03 13:13:22.000000000 +0100
--- ./src/include/nodes/parsenodes.h	2009-03-03 14:15:03.000000000 +0100
***************
*** 289,294 ****
--- 289,312 ----
  } 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
*** ./src/include/nodes/primnodes.h.orig	2009-03-04 14:12:16.000000000 +0100
--- ./src/include/nodes/primnodes.h	2009-03-04 14:13:43.000000000 +0100
***************
*** 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
   */
***************
*** 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;
*** ./src/include/parser/parse_func.h.orig	2009-03-03 14:16:15.000000000 +0100
--- ./src/include/parser/parse_func.h	2009-03-04 11:52:54.000000000 +0100
***************
*** 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,
*** ./src/test/regress/expected/polymorphism.out.orig	2009-03-04 18:57:59.000000000 +0100
--- ./src/test/regress/expected/polymorphism.out	2009-03-04 18:57:14.000000000 +0100
***************
*** 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);
*** ./src/test/regress/sql/polymorphism.sql.orig	2009-03-04 18:48:30.000000000 +0100
--- ./src/test/regress/sql/polymorphism.sql	2009-03-04 18:56:35.000000000 +0100
***************
*** 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);
#2Bernd Helmle
mailings@oopsware.de
In reply to: Pavel Stehule (#1)
1 attachment(s)
Re: mixed, named notation support

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@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

Attachments:

named_notation_20090717_review1.difftext/x-diff; charset=utf-8; name=named_notation_20090717_review1.diffDownload
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, &param_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, &param_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);
#3Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#2)
Re: mixed, named notation support

2009/7/18 Bernd Helmle <mailings@oopsware.de>:

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@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?

sure, please. Doc needs mainly language correction - my English is
bad. And maybe some others - from me is too short and too technical.

Pavel

Show quoted text

--
 Thanks

                  Bernd

#4Bernd Helmle
mailings@oopsware.de
In reply to: Pavel Stehule (#1)
Re: mixed, named notation support

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

Hello

I did some cleaning on this feature, and I hope so I solve some Tom's
objections

features:
* PostgreSQL's specific syntax for named parameter: value AS name,
* Doesn't change rules for defaults,
* Get defaults for named, mixed notation in planner time.

Pavel, consider the following function:

CREATE OR REPLACE FUNCTION ftest(a int, b text)
RETURNS RECORD
LANGUAGE SQL
AS
$$
SELECT $1, $2 ;
$$;

#= SELECT ftest('blubb' AS b, 128 AS a);
ERROR: function ftest(unknown, integer) does not exist at character 8

#= SELECT ftest(128 AS a, 'abcd' AS b);
ftest
------------
(128,abcd)
(1 row)

Isn't the first one supposed to work?

--
Thanks

Bernd

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#4)
Re: mixed, named notation support

2009/7/23 Bernd Helmle <mailings@oopsware.de>:

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

Hello

I did some cleaning on this feature, and I hope so I solve some Tom's
objections

features:
 * PostgreSQL's specific syntax for named parameter: value AS name,
 * Doesn't change rules for defaults,
 * Get defaults for named, mixed notation in planner time.

Pavel, consider the following function:

CREATE OR REPLACE FUNCTION ftest(a int, b text)
RETURNS RECORD
LANGUAGE SQL
AS
$$
      SELECT $1, $2 ;
$$;

#= SELECT ftest('blubb' AS b, 128 AS a);
ERROR:  function ftest(unknown, integer) does not exist at character 8

#= SELECT ftest(128 AS a, 'abcd' AS b);
 ftest
------------
(128,abcd)
(1 row)

Isn't the first one supposed to work?

it is probably bug. I'll look on it tomorrow.

Pavel

Show quoted text

--
 Thanks

                  Bernd

#6Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#5)
1 attachment(s)
Re: mixed, named notation support

Hello,

fixed patch attached + more regress tests.

Regards
Pavel Stehule

2009/7/23 Pavel Stehule <pavel.stehule@gmail.com>:

Show quoted text

2009/7/23 Bernd Helmle <mailings@oopsware.de>:

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

Hello

I did some cleaning on this feature, and I hope so I solve some Tom's
objections

features:
 * PostgreSQL's specific syntax for named parameter: value AS name,
 * Doesn't change rules for defaults,
 * Get defaults for named, mixed notation in planner time.

Pavel, consider the following function:

CREATE OR REPLACE FUNCTION ftest(a int, b text)
RETURNS RECORD
LANGUAGE SQL
AS
$$
      SELECT $1, $2 ;
$$;

#= SELECT ftest('blubb' AS b, 128 AS a);
ERROR:  function ftest(unknown, integer) does not exist at character 8

#= SELECT ftest(128 AS a, 'abcd' AS b);
 ftest
------------
(128,abcd)
(1 row)

Isn't the first one supposed to work?

it is probably bug. I'll look on it tomorrow.

Pavel

--
 Thanks

                  Bernd

Attachments:

named-fixed.diff.gzapplication/x-gzip; name=named-fixed.diff.gzDownload
#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#6)
1 attachment(s)
Re: mixed, named notation support

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Regards
Pavel

2009/7/25 Pavel Stehule <pavel.stehule@gmail.com>:

Show quoted text

Hello,

fixed patch attached + more regress tests.

Regards
Pavel Stehule

2009/7/23 Pavel Stehule <pavel.stehule@gmail.com>:

2009/7/23 Bernd Helmle <mailings@oopsware.de>:

--On Donnerstag, März 05, 2009 08:41:28 +0100 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

Hello

I did some cleaning on this feature, and I hope so I solve some Tom's
objections

features:
 * PostgreSQL's specific syntax for named parameter: value AS name,
 * Doesn't change rules for defaults,
 * Get defaults for named, mixed notation in planner time.

Pavel, consider the following function:

CREATE OR REPLACE FUNCTION ftest(a int, b text)
RETURNS RECORD
LANGUAGE SQL
AS
$$
      SELECT $1, $2 ;
$$;

#= SELECT ftest('blubb' AS b, 128 AS a);
ERROR:  function ftest(unknown, integer) does not exist at character 8

#= SELECT ftest(128 AS a, 'abcd' AS b);
 ftest
------------
(128,abcd)
(1 row)

Isn't the first one supposed to work?

it is probably bug. I'll look on it tomorrow.

Pavel

--
 Thanks

                  Bernd

Attachments:

named-fixed.diff.gzapplication/x-gzip; name=named-fixed.diff.gzDownload
#8Bernd Helmle
mailings@oopsware.de
In reply to: Pavel Stehule (#7)
Re: mixed, named notation support

--On Sonntag, Juli 26, 2009 06:17:49 +0200 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

--
Thanks

Bernd

#9Bernd Helmle
mailings@oopsware.de
In reply to: Bernd Helmle (#8)
1 attachment(s)
Re: mixed, named notation support

--On Montag, Juli 27, 2009 15:24:12 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

Looks good now.

Here is a slightly edited reviewed patch version. I've edited the docs and
fixed some spelling errors in a few error messages. It seems the patch
mangled some source comments in namespace.c, i've rearranged them and tried
to explain the purpose of VerifyCandidateNamedNotation(). I'm continuing
reviewing this but can't get on it again until sunday.

Pavel, can you have a look at the docs part of this patch and watch out for
any gotchas?

--
Thanks

Bernd

Attachments:

named_and_mixed_notation_review2.patch.gzapplication/octet-stream; name=named_and_mixed_notation_review2.patch.gzDownload
#10Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#9)
Re: mixed, named notation support

2009/7/31 Bernd Helmle <mailings@oopsware.de>:

--On Montag, Juli 27, 2009 15:24:12 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

Looks good now.

Here is a slightly edited reviewed patch version. I've edited the docs and
fixed some spelling errors in a few error messages. It seems the patch
mangled some source comments in namespace.c, i've rearranged them and tried
to explain the purpose of VerifyCandidateNamedNotation(). I'm continuing
reviewing this but can't get on it again until sunday.

Pavel, can you have a look at the docs part of this patch and watch out for
any gotchas?

Today, I'll look on it.

Pavel

Show quoted text

--
 Thanks

                  Bernd

#11Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#9)
Re: mixed, named notation support

2009/7/31 Bernd Helmle <mailings@oopsware.de>:

--On Montag, Juli 27, 2009 15:24:12 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

Looks good now.

Here is a slightly edited reviewed patch version. I've edited the docs and
fixed some spelling errors in a few error messages. It seems the patch
mangled some source comments in namespace.c, i've rearranged them and tried
to explain the purpose of VerifyCandidateNamedNotation(). I'm continuing
reviewing this but can't get on it again until sunday.

Pavel, can you have a look at the docs part of this patch and watch out for
any gotchas?

I looked there and I thing it's very good. I have only one idea about
samples in docs. Mathematical function isn't too much readable. Maybe
some string concation or returning record should be more readable.

create or replace function foo(a varchar, b varchar, uppercase boolean = false)
returns varchar as $$
select when uppercase then 'a=' || a ', b=' || b
else upper('a=' || a ', b=' || b) end;
$$ language sql immutable strict;

select foo('Hello','World');
select foo('Hello', World', true);
select foo('Hello' as a, 'World' as b);
select foo('Hello', 'World', true as uppercase);
select foo(true as uppercase, 'World' as b, 'Hello' as b);

or some similar

Thank You very much
Pavel

Show quoted text

--
 Thanks

                  Bernd

#12Robert Haas
robertmhaas@gmail.com
In reply to: Pavel Stehule (#11)
Re: mixed, named notation support

On Fri, Jul 31, 2009 at 8:46 AM, Pavel Stehule<pavel.stehule@gmail.com> wrote:

2009/7/31 Bernd Helmle <mailings@oopsware.de>:

--On Montag, Juli 27, 2009 15:24:12 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

Looks good now.

Here is a slightly edited reviewed patch version. I've edited the docs and
fixed some spelling errors in a few error messages. It seems the patch
mangled some source comments in namespace.c, i've rearranged them and tried
to explain the purpose of VerifyCandidateNamedNotation(). I'm continuing
reviewing this but can't get on it again until sunday.

Pavel, can you have a look at the docs part of this patch and watch out for
any gotchas?

I looked there and I thing it's very good. I have only one idea about
samples in docs. Mathematical function isn't too much readable. Maybe
some string concation or returning record should be more readable.

create or replace function foo(a varchar, b varchar, uppercase boolean = false)
returns varchar as $$
 select when uppercase then 'a=' || a ', b=' || b
                                      else upper('a=' || a ', b=' || b) end;
$$ language sql immutable strict;

select foo('Hello','World');
select foo('Hello', World', true);
select foo('Hello' as a, 'World' as b);
select foo('Hello', 'World', true as uppercase);
select foo(true as uppercase, 'World' as b, 'Hello' as b);

or some similar

Thank You very much
Pavel

What's the current status of this patch? Is someone (either Pavel or
Bernd) planning to update it further, or should it be marked Ready for
Committer?

Thanks,

...Robert

#13Pavel Stehule
pavel.stehule@gmail.com
In reply to: Robert Haas (#12)
Re: mixed, named notation support

2009/8/2 Robert Haas <robertmhaas@gmail.com>:

On Fri, Jul 31, 2009 at 8:46 AM, Pavel Stehule<pavel.stehule@gmail.com> wrote:

2009/7/31 Bernd Helmle <mailings@oopsware.de>:

--On Montag, Juli 27, 2009 15:24:12 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Hi,

I sending a little bit modified version - I removed my forgotten
comment in gram.y

Thanks, i'll look on it asap.

Looks good now.

Here is a slightly edited reviewed patch version. I've edited the docs and
fixed some spelling errors in a few error messages. It seems the patch
mangled some source comments in namespace.c, i've rearranged them and tried
to explain the purpose of VerifyCandidateNamedNotation(). I'm continuing
reviewing this but can't get on it again until sunday.

Pavel, can you have a look at the docs part of this patch and watch out for
any gotchas?

I looked there and I thing it's very good. I have only one idea about
samples in docs. Mathematical function isn't too much readable. Maybe
some string concation or returning record should be more readable.

create or replace function foo(a varchar, b varchar, uppercase boolean = false)
returns varchar as $$
 select when uppercase then 'a=' || a ', b=' || b
                                      else upper('a=' || a ', b=' || b) end;
$$ language sql immutable strict;

select foo('Hello','World');
select foo('Hello', World', true);
select foo('Hello' as a, 'World' as b);
select foo('Hello', 'World', true as uppercase);
select foo(true as uppercase, 'World' as b, 'Hello' as b);

or some similar

Thank You very much
Pavel

What's the current status of this patch?  Is someone (either Pavel or
Bernd) planning to update it further, or should it be marked Ready for
Committer?

Hello

Bernard wrote so will look on it today - I expect so there will be
only cosmetic changes in doc.

Pavel

Show quoted text

Thanks,

...Robert

#14Bernd Helmle
mailings@oopsware.de
In reply to: Robert Haas (#12)
Re: mixed, named notation support

--On Sonntag, August 02, 2009 11:38:22 -0400 Robert Haas
<robertmhaas@gmail.com> wrote:

What's the current status of this patch? Is someone (either Pavel or
Bernd) planning to update it further, or should it be marked Ready for
Committer?

I will incorporate some additional docs adjustments per Pavels suggestion
and will then mark it ready for committer review.

Please note that there are actually two patches involved here: one is
Pavels' patch the other is Steve Prentice' enhancement to allow named
notation in plpgsql.

--
Thanks

Bernd

#15Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#14)
Re: mixed, named notation support

2009/8/3 Bernd Helmle <mailings@oopsware.de>:

--On Sonntag, August 02, 2009 11:38:22 -0400 Robert Haas
<robertmhaas@gmail.com> wrote:

What's the current status of this patch?  Is someone (either Pavel or
Bernd) planning to update it further, or should it be marked Ready for
Committer?

I will incorporate some additional docs adjustments per Pavels suggestion
and will then mark it ready for committer review.

Please note that there are actually two patches involved here: one is
Pavels' patch the other is Steve Prentice' enhancement to allow named
notation in plpgsql.

I should to wait with Steve patch - I would to add main sql parser
into plpgsql - than Steve's patch is unnecessary. But if there will be
some problems, then we can use Steve's patch. It is simple - so there
are not big problems with commit.

Show quoted text

--
 Thanks

                  Bernd

#16Steve Prentice
prentice@cisco.com
In reply to: Pavel Stehule (#15)
Re: mixed, named notation support

On Aug 3, 2009, at 1:41 AM, Pavel Stehule wrote:

I should to wait with Steve patch - I would to add main sql parser
into plpgsql - than Steve's patch is unnecessary. But if there will be
some problems, then we can use Steve's patch. It is simple - so there
are not big problems with commit.

I was hoping we could get the small patch into plpgsql during this
commitfest. This makes plpgsql recognize 'AS' and not replace named
parameter labels with the variable reference. I understand there is an
effort underway to redo the plpgsql parser, but getting these two
patches in together will allow people to start playing with plpgsql +
named parameters at the end the of commitfest when the first alpha is
released. (You can use named parameters + plpgsql without this patch,
but not without some pretty serious limitations.)

Without this patch, this will fail:

create function create_user(alias text, display_name text) returns
void as $$
BEGIN
perform create_alias(alias AS alias);
...
END
$$ language plpgsql;

This is a common pattern for many of the stored procedures we are
porting and I'd imagine it's common elsewhere too. If the plpgsql
parser patch lands, this patch won't be needed, but it's hard to
predict when it will land.

As an aside, this pattern really shows how confusing the AS syntax can
be for named parameters. Which side is the label and which is the value?

Thanks,
-Steve

#17Pavel Stehule
pavel.stehule@gmail.com
In reply to: Steve Prentice (#16)
Re: mixed, named notation support

2009/8/3 Steve Prentice <prentice@cisco.com>:

On Aug 3, 2009, at 1:41 AM, Pavel Stehule wrote:

I should to wait with Steve patch - I would to add main sql parser
into plpgsql - than Steve's patch is unnecessary. But if there will be
some problems, then we can use Steve's patch. It is simple - so there
are not big problems with commit.

I was hoping we could get the small patch into plpgsql during this
commitfest. This makes plpgsql recognize 'AS' and not replace named
parameter labels with the variable reference. I understand there is an
effort underway to redo the plpgsql parser, but getting these two patches in
together will allow people to start playing with plpgsql + named parameters
at the end the of commitfest when the first alpha is released. (You can use
named parameters + plpgsql without this patch, but not without some pretty
serious limitations.)

Without this patch, this will fail:

create function create_user(alias text, display_name text) returns void as
$$
 BEGIN
   perform create_alias(alias AS alias);
   ...
 END
$$ language plpgsql;

This is a common pattern for many of the stored procedures we are porting
and I'd imagine it's common elsewhere too. If the plpgsql parser patch
lands, this patch won't be needed, but it's hard to predict when it will
land.

As an aside, this pattern really shows how confusing the AS syntax can be
for named parameters. Which side is the label and which is the value?

ok - I don't though about it. My goal is integration main parser next
commitfest, but it is true, so somebody would to play with named
params now. Commiting of Steve's patch doesn't break anything.

Pavel

Show quoted text

Thanks,
-Steve

#18Robert Haas
robertmhaas@gmail.com
In reply to: Pavel Stehule (#17)
Re: mixed, named notation support

On Mon, Aug 3, 2009 at 10:51 AM, Pavel Stehule<pavel.stehule@gmail.com> wrote:

2009/8/3 Steve Prentice <prentice@cisco.com>:

On Aug 3, 2009, at 1:41 AM, Pavel Stehule wrote:

I should to wait with Steve patch - I would to add main sql parser
into plpgsql - than Steve's patch is unnecessary. But if there will be
some problems, then we can use Steve's patch. It is simple - so there
are not big problems with commit.

I was hoping we could get the small patch into plpgsql during this
commitfest. This makes plpgsql recognize 'AS' and not replace named
parameter labels with the variable reference. I understand there is an
effort underway to redo the plpgsql parser, but getting these two patches in
together will allow people to start playing with plpgsql + named parameters
at the end the of commitfest when the first alpha is released. (You can use
named parameters + plpgsql without this patch, but not without some pretty
serious limitations.)

Without this patch, this will fail:

create function create_user(alias text, display_name text) returns void as
$$
 BEGIN
   perform create_alias(alias AS alias);
   ...
 END
$$ language plpgsql;

This is a common pattern for many of the stored procedures we are porting
and I'd imagine it's common elsewhere too. If the plpgsql parser patch
lands, this patch won't be needed, but it's hard to predict when it will
land.

As an aside, this pattern really shows how confusing the AS syntax can be
for named parameters. Which side is the label and which is the value?

ok - I don't though about it. My goal is integration main parser next
commitfest, but it is true, so somebody would to play with named
params now. Commiting of Steve's patch doesn't break anything.

I'm a little confused here. We are 19 days into a 31 day CommitFest;
you are almost three weeks too late to add a patch to the queue.
Unless you can convince a friendly committer to pick this up out of
sequence, I think the only patch that is under consideration here is
the one that has been being discussed on this thread for the last 17
days. I sent several notes adding for all patches to be added to
commitfest.postgresql.org prior to the start of CommitFest; AFAIK,
this one was never added.

You haven't actually specified which other patch of Steve's you're
talking about anyway, but I'm guessing it's this one here:
http://archives.postgresql.org/pgsql-hackers/2009-05/msg00801.php

I don't think that patch would get applied even if HAD been added to
the CommitFest page in time, see Tom's remarks here:
http://archives.postgresql.org/pgsql-hackers/2009-05/msg00802.php

Let's get back to focusing on the patch that is actually under
consideration here.

...Robert

#19Pavel Stehule
pavel.stehule@gmail.com
In reply to: Robert Haas (#18)
Re: mixed, named notation support

ok - I don't though about it. My goal is integration main parser next
commitfest, but it is true, so somebody would to play with named
params now. Commiting of Steve's patch doesn't break anything.

I'm a little confused here.  We are 19 days into a 31 day CommitFest;
you are almost three weeks too late to add a patch to the queue.
Unless you can convince a friendly committer to pick this up out of
sequence, I think the only patch that is under consideration here is
the one that has been being discussed on this thread for the last 17
days.  I sent several notes adding for all patches to be added to
commitfest.postgresql.org prior to the start of CommitFest; AFAIK,
this one was never added.

You haven't actually specified which other patch of Steve's you're
talking about anyway, but I'm guessing it's this one here:
http://archives.postgresql.org/pgsql-hackers/2009-05/msg00801.php

I don't think that patch would get applied even if HAD been added to
the CommitFest page in time, see Tom's remarks here:
http://archives.postgresql.org/pgsql-hackers/2009-05/msg00802.php

Let's get back to focusing on the patch that is actually under
consideration here.

There are two patches - named and mixed parameters and protecting "AS"
as keyword. First patch is pl independed, second patch is related to
plpgsql. Both patches was in queue and Bernard did reviewing both I
thing. Second patch fix some unwanted behave in plpgsql and it does on
level that was possible on code base one month old.

I hope, so we will be able to integrate main parser to core. I hope so
this will be in next commitfest. If we doesn't release code after this
commitfest, then I'll prefer wait for integration, but now, when we
will release code, I thing, so for somebody should be nice to use
fixed plpgsql.

I thing so Steve fixed issues mentioned by Tom

http://archives.postgresql.org/pgsql-hackers/2009-05/msg00804.php

Integration of main parser to plpgsql will be very significant change
and it needs separate patch. This solve some others big problems
plpgsql and I would to solve it separately.

I'll be glad to answer all questions.

Pavel

Show quoted text

...Robert

#20Steve Prentice
prentice@cisco.com
In reply to: Robert Haas (#18)
Re: mixed, named notation support

On Aug 3, 2009, at 9:38 AM, Robert Haas wrote:

I sent several notes adding for all patches to be added to
commitfest.postgresql.org prior to the start of CommitFest; AFAIK,
this one was never added.

Hi Robert,

The patch for plpgsql was added as a comment to Pavel's patch. I added
it as a comment because it wouldn't make since to commit it or even
review it separately. This was done on the wiki before the migration.
Perhaps that was not the correct way to add it to the commitfest. If
not, my apologies.

-Steve

#21Bernd Helmle
mailings@oopsware.de
In reply to: Robert Haas (#18)
Re: mixed, named notation support

--On Montag, August 03, 2009 12:38:48 -0400 Robert Haas
<robertmhaas@gmail.com> wrote:

I'm a little confused here. We are 19 days into a 31 day CommitFest;
you are almost three weeks too late to add a patch to the queue.
Unless you can convince a friendly committer to pick this up out of
sequence, I think the only patch that is under consideration here is
the one that has been being discussed on this thread for the last 17
days. I sent several notes adding for all patches to be added to
commitfest.postgresql.org prior to the start of CommitFest; AFAIK,
this one was never added.

Well, i already noted that in the "named and mixed notation" thread
actually two patches were involved, see
<http://archives.postgresql.org/pgsql-rrreviewers/2009-07/msg00016.php&gt;.

Since Steve's changes were so small, i considered it to be an "extension"
to Pavels patch, without treating it separately to commit. In my opinion it
wouldn't make sense to commit those changes to plpgsql _without_ having
Pavels functionality. I have to admit that i saw Tom's complaint about
making AS a special keyword in plpgsql, but decided to leave the decision
wether we want to commit this or not up to a committer. I took the efforts
to get the attached patches there in a reviewable state then instead.

Please note that Steve's suggestion is linked into the commitfest since
2009-05-21, too.

--
Thanks

Bernd

#22Bernd Helmle
mailings@oopsware.de
In reply to: Steve Prentice (#20)
Re: mixed, named notation support

--On Montag, August 03, 2009 12:18:13 -0700 Steve Prentice
<prentice@cisco.com> wrote:

I added it as a comment because it wouldn't make since to commit it or
even review it separately.

That was exactly i was understanding it.

--
Thanks

Bernd

#23Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Bernd Helmle (#21)
Re: mixed, named notation support

Bernd Helmle <mailings@oopsware.de> wrote:

Please note that Steve's suggestion is linked into the commitfest
since 2009-05-21, too.

Yeah:

http://wiki.postgresql.org/index.php?title=CommitFest_2009-First&amp;diff=6391&amp;oldid=6250

-Kevin

#24Bernd Helmle
mailings@oopsware.de
In reply to: Robert Haas (#18)
Re: mixed, named notation support

--On Montag, August 03, 2009 12:38:48 -0400 Robert Haas
<robertmhaas@gmail.com> wrote:

Let's get back to focusing on the patch that is actually under
consideration here.

Status Report: I will finish documentation and review tomorrow and will
mark this patch for committer review.

--
Thanks

Bernd

#25Bernd Helmle
mailings@oopsware.de
In reply to: Bernd Helmle (#24)
1 attachment(s)
Re: mixed, named notation support

--On Montag, August 03, 2009 23:43:08 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Status Report: I will finish documentation and review tomorrow and will
mark this patch for committer review.

Here's my latest reviewed version of Pavel's patch with adjusted
documentation per latest discussion.

While poking a little bit with simplify_function() I realized that this
patch changes the behavior of VARIADIC functions a little bit. For example:

CREATE OR REPLACE FUNCTION my_test(a IN text, txt VARIADIC text[])
RETURNS text
AS
$$
SELECT $2[1];
$$ LANGUAGE SQL;

The following doesn't work in current 8.4:

SELECT my_test('abcd', ARRAY['test', 'foo']);

You need to use the VARIADIC keyword to match the second argument to
text[]. However, if you are going to use named notation with the patch
applied, the picture changes in HEAD:

SELECT my_test('abcd' AS a, ARRAY['test', 'foo'] AS txt);
my_test
---------
test
(1 row)

This applies also when you reverse the argument order. I don't know wether
this is intended, but its conflicting with what we have currently in the
docs. It's also not clear to me wether we want this at all.

--
Thanks

Bernd

Attachments:

named_and_mixed_notation_review3.patch.gzapplication/octet-stream; name=named_and_mixed_notation_review3.patch.gzDownload
#26Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#25)
Re: mixed, named notation support

2009/8/4 Bernd Helmle <mailings@oopsware.de>:

--On Montag, August 03, 2009 23:43:08 +0200 Bernd Helmle
<mailings@oopsware.de> wrote:

Status Report: I will finish documentation and review tomorrow and will
mark this patch for committer review.

Here's my latest reviewed version of Pavel's patch with adjusted
documentation per latest discussion.

While poking a little bit with simplify_function() I realized that this
patch changes the behavior of VARIADIC functions a little bit. For example:

CREATE OR REPLACE FUNCTION my_test(a IN text, txt VARIADIC text[])
RETURNS text
AS
$$
 SELECT $2[1];
$$ LANGUAGE SQL;

The following doesn't work in current 8.4:

SELECT my_test('abcd', ARRAY['test', 'foo']);

You need to use the VARIADIC keyword to match the second argument to text[].
However, if you are going to use named notation with the patch applied, the
picture changes in HEAD:

SELECT my_test('abcd' AS a, ARRAY['test', 'foo'] AS txt);
my_test
---------
test
(1 row)

This applies also when you reverse the argument order. I don't know wether
this is intended, but its conflicting with what we have currently in the
docs. It's also not clear to me wether we want this at all.

Named notation has different algorithm for function detection then
positional notation. There are not exist variadic parameters (because
these parameters hasn't individual names). So only "packed" variadic
parameter should be there, and this parameter have to be named - so
keyword VARIADIC is optional.

Thank You very much
Pavel

Show quoted text

--
 Thanks

                  Bernd

#27Bernd Helmle
mailings@oopsware.de
In reply to: Pavel Stehule (#26)
Re: mixed, named notation support

--On 4. August 2009 20:22:05 +0200 Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Named notation has different algorithm for function detection then
positional notation. There are not exist variadic parameters (because
these parameters hasn't individual names). So only "packed" variadic
parameter should be there, and this parameter have to be named - so
keyword VARIADIC is optional.

I wonder wether it wouldn't better to force positional notation for such
functions then. I found it surprising that this works at all, but of
course, someone else might enjoy this as a cool feature. To me, it feels
strange and confusing that a function declared as VARIADIC suddenly accepts
a "sloppy" argument only because you are using some other calling notation
where others enforces you to use an additional keyword to match the
function.

At least, we need to document that both notations behaves different in this
case.

--
Thanks

Bernd

#28Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#27)
Re: mixed, named notation support

2009/8/5 Bernd Helmle <mailings@oopsware.de>:

--On 4. August 2009 20:22:05 +0200 Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Named notation has different algorithm for function detection then
positional notation. There are not exist variadic parameters (because
these parameters hasn't individual names). So only "packed" variadic
parameter should be there, and this parameter have to be named  - so
keyword VARIADIC is optional.

I wonder wether it wouldn't better to force positional notation for such
functions then. I found it surprising that this works at all, but of course,
someone else might enjoy this as a cool feature. To me, it feels strange and
confusing that a function declared as VARIADIC suddenly accepts a "sloppy"
argument only because you are using some other calling notation where others
enforces you to use an additional keyword to match the function.

it's little bit difficult - notation is known from code, and isn't
possible change it..

At least, we need to document that both notations behaves different in this
case.

+1

some like - when notation or mixed notation are used, then variadic
parameters are disabled besause individual variadic parameters hasn't
name. You can put packed value to variadic parameter (when this
parameter is named) without keyword VARIADIC (against to positional
notation) - sample

CREATE OR REPLACE FUNCTION foo(a varchar, variadic b varchar[])
RETURNS varchar AS $$
SELECT a || ' ' || array_to_string(ARRAY(unnest b),' ')
$$ LANGUAGE sql;

SELECT foo('Hello', 'World','again'); -- positional notation
SELECT foo('Hello', VARIADIC ARRAY['World','again']) -- positional
notation with packed variadic parameter
SELECT foo('Hello', ARRAY['World','again'] AS b) -- mixed notation
with named packed variadic parameter

Pavel

Show quoted text

--
Thanks

       Bernd

#29Bernd Helmle
mailings@oopsware.de
In reply to: Pavel Stehule (#28)
1 attachment(s)
Re: mixed, named notation support

--On Mittwoch, August 05, 2009 05:28:55 +0200 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

At least, we need to document that both notations behaves different in
this case.

+1

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we have some
time left to incorporate additional feedback.

--
Thanks

Bernd

Attachments:

named_and_mixed_notation_review4.patch.gzapplication/octet-stream; name=named_and_mixed_notation_review4.patch.gzDownload
#30Pavel Stehule
pavel.stehule@gmail.com
In reply to: Bernd Helmle (#29)
Re: mixed, named notation support

2009/8/5 Bernd Helmle <mailings@oopsware.de>:

--On Mittwoch, August 05, 2009 05:28:55 +0200 Pavel Stehule
<pavel.stehule@gmail.com> wrote:

At least, we need to document that both notations behaves different in
this case.

+1

Here again a patch version with updated documentation. I will stop reviewing
this patch now and mark this ready for committer, so we have some time left
to incorporate additional feedback.

--
 Thanks

                  Bernd

my Thanks

Pavel

#31Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bernd Helmle (#29)
Re: mixed, named notation support

Bernd Helmle <mailings@oopsware.de> writes:

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we have some
time left to incorporate additional feedback.

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?". I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful. Can't you come up with something better than
"continuous/leaky"?

regards, tom lane

#32Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#31)
Re: mixed, named notation support

On Thu, Aug 6, 2009 at 7:10 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Bernd Helmle <mailings@oopsware.de> writes:

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we have some
time left to incorporate additional feedback.

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?".  I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful.  Can't you come up with something better than
"continuous/leaky"?

Stepping back a bit, are we sure this is a feature we even want to
support? It was already pointed out in the thread on "Parser's hook
based on funccall" that SQL:201x may standardize => for this purpose.
I realize that's a problem because of the possibility of a
user-defined operator named =>, but aren't we usually reluctant to
adopt syntax that is thought likely to be incompatible with current or
future SQL standards?

http://archives.postgresql.org/pgsql-hackers/2009-07/msg01715.php

...Robert

#33Steve Prentice
prentice@cisco.com
In reply to: Robert Haas (#32)
Re: mixed, named notation support

On Aug 6, 2009, at 7:12 PM, Robert Haas wrote:

On Thu, Aug 6, 2009 at 7:10 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Bernd Helmle <mailings@oopsware.de> writes:

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we
have some
time left to incorporate additional feedback.

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?". I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful. Can't you come up with something better than
"continuous/leaky"?

Stepping back a bit, are we sure this is a feature we even want to
support? It was already pointed out in the thread on "Parser's hook
based on funccall" that SQL:201x may standardize => for this purpose.
I realize that's a problem because of the possibility of a
user-defined operator named =>, but aren't we usually reluctant to
adopt syntax that is thought likely to be incompatible with current or
future SQL standards?

As a "newbie" to postgresql, I would hope this is a feature that will
be supported in the not too distant future. If the standard seems to
be moving in the direction of using 'name => value' as the syntax, it
does seem like that would be the way we would want to go. If I
remember correctly, the main argument for using "value AS name" was
that it wouldn't conflict with current operators AND it would be the
most likely way the standard body would go. (There was a long thread
back in Dec 08 regarding the syntax that can be referenced if someone
wants to read through all of them.)

If it looks like the SQL standard will be going the direction of 'name
=> value', why would we go opposite that?

Either way, I think Pavel has proven that it is easy to adjust his
patch to support either syntax if a decision is made.

-Steve

#34Pavel Stehule
pavel.stehule@gmail.com
In reply to: Robert Haas (#32)
Re: mixed, named notation support

2009/8/7 Robert Haas <robertmhaas@gmail.com>:

On Thu, Aug 6, 2009 at 7:10 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Bernd Helmle <mailings@oopsware.de> writes:

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we have some
time left to incorporate additional feedback.

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?".  I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful.  Can't you come up with something better than
"continuous/leaky"?

Stepping back a bit, are we sure this is a feature we even want to
support?  It was already pointed out in the thread on "Parser's hook
based on funccall" that SQL:201x may standardize => for this purpose.
I realize that's a problem because of the possibility of a
user-defined operator named =>, but aren't we usually reluctant to
adopt syntax that is thought likely to be incompatible with current or
future SQL standards?

http://archives.postgresql.org/pgsql-hackers/2009-07/msg01715.php

...Robert

We should support both syntax - in future. I afraid so we will thing
up nothing new now. The arguments against to '=>' are valid still.
This syntax (with "AS") doesn't break anything in future. PostgreSQL
could to support both (default with AS) and via GUC standard (like
standard_conforming_strings)

Pavel

Show quoted text
#35Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#31)
Re: mixed, named notation support

2009/8/7 Tom Lane <tgl@sss.pgh.pa.us>:

Bernd Helmle <mailings@oopsware.de> writes:

Here again a patch version with updated documentation. I will stop
reviewing this patch now and mark this ready for committer, so we have some
time left to incorporate additional feedback.

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?".  I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful.  Can't you come up with something better than
"continuous/leaky"?

It's mean so there are some gaps in arg list and these gaps have to be
filled from defaults.

I am sorry, I am not native speaker, so in good names for identifiers
are really bad. I am searching in dictionary - maybe incomplete or
fragment, ...

regards
Pavel Stehule

Show quoted text

                       regards, tom lane

#36Bernd Helmle
mailings@oopsware.de
In reply to: Tom Lane (#31)
Re: mixed, named notation support

--On Donnerstag, August 06, 2009 19:10:47 -0400 Tom Lane
<tgl@sss.pgh.pa.us> wrote:

I'm starting to look at this now, and my very first reaction was
"what in the world is a leaky list?". I'm not sure I like the
data structure itself, but the terminology is certainly completely
unhelpful.

Hrm, i thought i put a comment in there, but it seems i forgot it, sorry. A
leaky list seems to be a list to remember where to fill in DEFAULT argument
values.

Maybe arg_values with ARGS_NAMED_NOTATION and ARGS_POSITIONAL_NOTATION is a
better notion.

--
Thanks

Bernd

#37Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#32)
Re: mixed, named notation support

Robert Haas <robertmhaas@gmail.com> writes:

Stepping back a bit, are we sure this is a feature we even want to
support? It was already pointed out in the thread on "Parser's hook
based on funccall" that SQL:201x may standardize => for this purpose.

Absolutely no evidence has been presented that the SQL committee is even
going to standardize something in this area, much less that they are
likely to adopt => as the syntax. I think it would be completely out of
character for them to do that --- their entire body of work over the
past twenty years has been reflective of a COBOL-ish approach to syntax,
ie use keywords not punctuation. Look at the built-in functions like
SUBSTRING, POSITION, TREAT; or what they did to introduce window
functions.

There is definitely not enough evidence here to justify breaking
existing applications, which is what introducing => would do.
When and if there's a ratified standard using =>, it'll be time
to break stuff. In the meantime we can do something with AS and
be reasonably certain we haven't painted ourselves into a corner.

regards, tom lane

#38Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#37)
Re: mixed, named notation support

On Fri, Aug 7, 2009 at 12:04 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

There is definitely not enough evidence here to justify breaking
existing applications, which is what introducing => would do.
When and if there's a ratified standard using =>, it'll be time
to break stuff.  In the meantime we can do something with AS and
be reasonably certain we haven't painted ourselves into a corner.

I wasn't proposing introducing => ; I thought we might want to hold
off on doing anything at all. But I may be the only one, so never
mind.

...Robert

#39Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#38)
Re: mixed, named notation support

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)? ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Considering that we are worried about someday having to adjust to a
SQL standard in this area, I think we ought to be as conservative as
possible about what we introduce as user-visible features here.
As an example, if they do go with "=>" as the parameter marker,
mixed notation would become a seriously bad idea because it would be
impossible to distinguish incidental use of => as an operator from
mixed notation.

regards, tom lane

#40Bernd Helmle
mailings@oopsware.de
In reply to: Tom Lane (#39)
Re: mixed, named notation support

--On 9. August 2009 12:27:53 -0400 Tom Lane <tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)? ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Hmm, Oracle has started supporting it in recent versions, too. So one
advantage would be at least some sort of compatibility for another favorite
database.

From a user's point of view, i see one use case in calling functions with

multiple default argument values, where only one of those value needs to be
overwritten, e.g.

SELECT foo(1, 100, 'this' AS one);
SELECT foo(1, 102, 'other' AS two);
SELECT foo(1, 100, 'another' AS three);

where one, two, three are arguments with specific default values.

--
Thanks

Bernd

#41Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bernd Helmle (#40)
Re: mixed, named notation support

Bernd Helmle <mailings@oopsware.de> writes:

--On 9. August 2009 12:27:53 -0400 Tom Lane <tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)? ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Hmm, Oracle has started supporting it in recent versions, too. So one
advantage would be at least some sort of compatibility for another favorite
database.

Mph. Does Oracle adopt the same semantics for what a mixed call means?
Because my next complaint was going to be that this definition was
poorly chosen anyway --- it seems confusing, unintuitive, and
restrictive. If the function is defined as having parameters (a,b,c)
then what does this do:

select foo(1, 2, 3 as b);

and what's the argument for having it do that rather than something
else?

If the behavior isn't *exactly* like Oracle in corner cases like this,
I think partial compatibility is worse than none. And in any case the
point stands that the more behavior you invent here, the more likely
you are to get blindsided by the eventual SQL standard.

regards, tom lane

#42Bernd Helmle
mailings@oopsware.de
In reply to: Tom Lane (#41)
Re: mixed, named notation support

--On 9. August 2009 13:00:07 -0400 Tom Lane <tgl@sss.pgh.pa.us> wrote:

Mph. Does Oracle adopt the same semantics for what a mixed call means?

I had a look at the Oracle documentation while reviewing this patch, and i
thought we are pretty close to what they do. Maybe Pavel can comment more
on it.

Because my next complaint was going to be that this definition was
poorly chosen anyway --- it seems confusing, unintuitive, and
restrictive. If the function is defined as having parameters (a,b,c)
then what does this do:

select foo(1, 2, 3 as b);

and what's the argument for having it do that rather than something
else?

Since b is ambiguous we error out (I don't know what Oracle does, but i
would be surprised if they do anything different).

--
Thanks

Bernd

#43Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#39)
Re: mixed, named notation support

On Sun, Aug 9, 2009 at 12:27 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)?  ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Wow, I can't imagine not supporting that. Doesn't every language that
supports anything like named parameters also support a mix? LISP is
the obvious example, but I think most major scripting languages (Perl,
Ruby, etc.) support something akin to this through a trailing hash
argument. C# apparently supports both for something called attribute
classes (don't ask me what they are). Ada also supports both styles,
and you can mix them.

http://msdn.microsoft.com/en-us/library/aa664614(VS.71).aspx
http://goanna.cs.rmit.edu.au/~dale/ada/aln/8_subprograms.html#RTFToC11

The case where this is really useful is when you have lots of
parameters most of which don't need to be set to a non-default value
most of the time, but a few of which always need to be specified. In
C you might do something like this:

int frobozz(int common1, int common2, struct frobozz_options *fopt);
/* fopt == NULL for defaults */
void init_frobozz_options(struct frobozz_options *);

...but the named/mixed syntax is more compact, and certainly nice to
have, even if I'm emphatically not in love with the syntax we're stuck
with.

ISTM that the Ada rule that all positional must proceed all named is a
good one, and to resolve the problem you're talking about here it
seems we should probably also make a rule that specifying a value for
the same parameter more than once (either both position and named, or
named twice) is an error.

It would also be quite reasonable to impose a requirement that
parameters can only be specified using named notation if it has been
explicitly enabled for that particular parameter. In LISP, there are
four kinds of parameters (required, optional, rest, keyword) and any
particular parameter gets to be exactly one of those four things. The
rule for resolving ambiguities may not be what you want, but it's
well-defined and pretty reasonable.

http://gigamonkeys.com/book/functions.html#mixing-different-parameter-types

...Robert

#44Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#43)
Re: mixed, named notation support

Robert Haas <robertmhaas@gmail.com> writes:

On Sun, Aug 9, 2009 at 12:27 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)? ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Wow, I can't imagine not supporting that. Doesn't every language that
supports anything like named parameters also support a mix?

I don't know if that's true, and I definitely don't know if they all
handle corner cases identically. I think this patch is an exercise in
guessing at what the SQL committee will eventually do, and as such, we
should avoid like the plague making any guesses that carry significant
risk of being semantically incompatible with what they eventually do.
The risk/reward ratio isn't good enough.

regards, tom lane

#45Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#44)
Re: mixed, named notation support

On Sun, Aug 9, 2009 at 2:17 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Sun, Aug 9, 2009 at 12:27 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)?  ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

Wow, I can't imagine not supporting that.  Doesn't every language that
supports anything like named parameters also support a mix?

I don't know if that's true, [...]

I'm fairly sure it is.

and I definitely don't know if they all
handle corner cases identically.

I'm 100% positive that they don't.

I think this patch is an exercise in
guessing at what the SQL committee will eventually do, and as such, we
should avoid like the plague making any guesses that carry significant
risk of being semantically incompatible with what they eventually do.
The risk/reward ratio isn't good enough.

I completely agree; I would have chosen to boot the entire patch for
exactly that reason. However, given that you don't seem to want to do
that, I was just offering my thoughts on the various possible methods
of eliminating that risk, since the one you're suggesting doesn't seem
ideal to me. I thought that perhaps providing some examples of how
other programming languages handle this problem might be helpful - in
particular, I think the Lisp model, where each parameter must be
EITHER named or positional, has a lot to recommend it.

There is little doubt here that what gets committed here will be what
you choose to commit.

...Robert

#46Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#45)
Re: mixed, named notation support

Robert Haas <robertmhaas@gmail.com> writes:

On Sun, Aug 9, 2009 at 2:17 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

I think this patch is an exercise in
guessing at what the SQL committee will eventually do, and as such, we
should avoid like the plague making any guesses that carry significant
risk of being semantically incompatible with what they eventually do.
The risk/reward ratio isn't good enough.

I completely agree; I would have chosen to boot the entire patch for
exactly that reason.

Well, that's certainly still an available option. But people have been
asking for this type of feature for a long time. I think we should be
willing to include something along this line. What I don't want to do
is include something that might be semantically incompatible with the
eventual standard --- by which I mean accepting a call that the standard
also accepts, but specifies doing something different than what we do.
I'd prefer to omit inessential functionality rather than risk that.

It might be that the patch does, or can be made to, throw error in any
case that's even slightly questionable, while still allowing mixed cases
that seem certain to have only one possible interpretation. But I'm not
convinced that's where it's at today.

regards, tom lane

#47Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#39)
Re: mixed, named notation support

Hello

2009/8/9 Tom Lane <tgl@sss.pgh.pa.us>:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters named
and some not)?  ISTM that just serves to complicate both the patch
and the user's-eye view, for no real benefit.

consider function like

foo(mandatory params without defaults, optional params with defaults)

because you have to put all mandatory params, then names are needless.
But some optional params you have to specify by names, because without
names, you have to put full params set with respect to rule about
using default params.

CREATE OR REPLACE FUNCTION strtr(a varchar, uppercase boolean = false,
lowercase boolean = false) RETURNS varchar AS $$
BEGIN
IF uppercase and lowercase THEN RAISE EXCEPTION 'cannot use both
modificator together' END IF;
IF uppercase THEN RETURN upper(a); END IF;
IF lowercase THEN RETURN lower(a); END IF;
RETURN a;
END IF;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;

the advice is verbosity:

SELECT strtr('some text',true, false);
versus
SELECT strtr('some text', true AS uppercase);
or
SELECT strtr('some text', true AS lowercase);

With mixed notation is very clean border betwenn mandatory and optional params.

I thing, so without mixed notation this patch hasn't any sense and I
thing it's better to drop it.

Considering that we are worried about someday having to adjust to a
SQL standard in this area, I think we ought to be as conservative as
possible about what we introduce as user-visible features here.
As an example, if they do go with "=>" as the parameter marker,
mixed notation would become a seriously bad idea because it would be
impossible to distinguish incidental use of => as an operator from
mixed notation.

I am sorry, I don't understand. When => should be some operator, then
you cannot distinguish between named notation and positional notation
too. Mixed notation doesn't play any role.

foo(a => 10, b=>20) should be code in positional notation much like
named notation. ??? How is difference?

I thing so when some body use operator =>, then he have to break
standard notation for some collision situation or for all situation.
Syntax with AS is safe and should be enabled anywhere. We can simply
detect situation where operator => exists and standard named
parameters are allowed.

I thing, so we are on safe side, because we should to support both
syntax, and can disable temporary one ambiguous.

regards
Pavel Stehule

Show quoted text

                       regards, tom lane

#48Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#47)
Re: mixed, named notation support

Considering that we are worried about someday having to adjust to a
SQL standard in this area, I think we ought to be as conservative as
possible about what we introduce as user-visible features here.
As an example, if they do go with "=>" as the parameter marker,
mixed notation would become a seriously bad idea because it would be
impossible to distinguish incidental use of => as an operator from
mixed notation.

I thing, so ANSI will be in conformance with Oracle - so I'll try to
check the possibility of the using => as any operator in Oracle

regards
Pavel Stehule

#49Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Robert Haas (#43)
Re: mixed, named notation support

Robert Haas <robertmhaas@gmail.com> wrote:

On Sun, Aug 9, 2009 at 12:27 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:

Now that I've started to read this patch ... exactly what is the
argument for allowing a "mixed" notation (some of the parameters
named and some not)? ISTM that just serves to complicate both the
patch and the user's-eye view, for no real benefit.

Wow, I can't imagine not supporting that. Doesn't every language
that supports anything like named parameters also support a mix?

Sybase ASE and Microsoft SQL Server support mixed notation (and I
think that goes back to their common version 1.0). If a parameter is
specified more than once, it is an error.

-Kevin