diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
new file mode 100644
index ce3b77b..be71fbb
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
*************** typedef SQLFunctionCache *SQLFunctionCac
*** 116,122 ****
--- 116,124 ----
   */
  typedef struct SQLFunctionParseInfo
  {
+ 	char	   *name;			/* function's name */
  	Oid		   *argtypes;		/* resolved types of input arguments */
+ 	char	  **argnames;		/* names of input arguments */
  	int			nargs;			/* number of input arguments */
  	Oid			collation;		/* function's input collation, if known */
  } SQLFunctionParseInfo;
*************** typedef struct SQLFunctionParseInfo
*** 124,129 ****
--- 126,133 ----
  
  /* non-export function prototypes */
  static Node *sql_fn_param_ref(ParseState *pstate, ParamRef *pref);
+ static Node *sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var);
+ static Node *sql_fn_param_ref_num(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, int paramno, int location);
  static List *init_execution_state(List *queryTree_list,
  					 SQLFunctionCachePtr fcache,
  					 bool lazyEvalOK);
*************** prepare_sql_fn_parse_info(HeapTuple proc
*** 163,168 ****
--- 167,173 ----
  	int			nargs;
  
  	pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo));
+ 	pinfo->name = NameStr(procedureStruct->proname);
  
  	/* Save the function's input collation */
  	pinfo->collation = inputCollation;
*************** prepare_sql_fn_parse_info(HeapTuple proc
*** 201,206 ****
--- 206,241 ----
  		pinfo->argtypes = argOidVect;
  	}
  
+ 	if (nargs > 0)
+ 	{
+ 		Datum		proargnames;
+ 		Datum		proargmodes;
+ 		int			argnum;
+ 		int			n_arg_names;
+ 		bool		isNull;
+ 
+ 		proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+ 									  Anum_pg_proc_proargnames,
+ 									  &isNull);
+ 		if (isNull)
+ 			proargmodes = PointerGetDatum(NULL);	/* just to be sure */
+ 
+ 		proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+ 									  Anum_pg_proc_proargmodes,
+ 									  &isNull);
+ 		if (isNull)
+ 			proargmodes = PointerGetDatum(NULL);	/* just to be sure */
+ 
+ 		n_arg_names = get_func_input_arg_names(proargnames, proargmodes, &pinfo->argnames);
+ 
+ 		if (n_arg_names < nargs)
+ 			pinfo->argnames = NULL;
+ 	}
+ 	else
+ 	{
+ 		pinfo->argnames = NULL;
+ 	}
+ 
  	return pinfo;
  }
  
*************** prepare_sql_fn_parse_info(HeapTuple proc
*** 210,223 ****
  void
  sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
  {
- 	/* Later we might use these hooks to support parameter names */
  	pstate->p_pre_columnref_hook = NULL;
! 	pstate->p_post_columnref_hook = NULL;
  	pstate->p_paramref_hook = sql_fn_param_ref;
  	/* no need to use p_coerce_param_hook */
  	pstate->p_ref_hook_state = (void *) pinfo;
  }
  
  /*
   * sql_fn_param_ref		parser callback for ParamRefs ($n symbols)
   */
--- 245,354 ----
  void
  sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
  {
  	pstate->p_pre_columnref_hook = NULL;
! 	pstate->p_post_columnref_hook = sql_fn_post_column_ref;
  	pstate->p_paramref_hook = sql_fn_param_ref;
  	/* no need to use p_coerce_param_hook */
  	pstate->p_ref_hook_state = (void *) pinfo;
  }
  
+ static Node *
+ sql_fn_resolve_name(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, const char *paramname, int location)
+ {
+ 	int		i;
+ 	for (i = 0; i < pinfo->nargs; i++)
+ 		if (pinfo->argnames[i] && strcmp(pinfo->argnames[i], paramname) == 0)
+ 			return sql_fn_param_ref_num(pstate, pinfo, i + 1, location);
+ 
+ 	return NULL;
+ }
+ 
+ /*
+  * sql_fn_post_column_ref		parser callback for ColumnRefs
+  */
+ static Node *
+ sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
+ {
+ 	SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
+ 	int			names;
+ 	Node	   *field1;
+ 	Node	   *subfield = NULL;
+ 	const char *pname;
+ 	Node	   *param;
+ 
+ 	if (var != NULL)
+ 		return NULL;			/* there's a table column, prefer that */
+ 
+ 	/*
+ 	 * The allowed syntaxes are:
+ 	 *
+ 	 * A		A = parameter name
+ 	 * A.B		A = (record-typed) parameter name, B = field reference,
+ 	 *			OR A = function name, B = parameter name
+ 	 * A.B.C	A = function name, B = (record-typed) parameter name,
+ 	 *			C = field reference
+ 	 */
+ 	names = list_length(cref->fields);
+ 
+ 	if (names > 3)
+ 		return NULL;
+ 
+ 	field1 = (Node *) linitial(cref->fields);
+ 	if (names > 1)
+ 		subfield = (Node *) lsecond(cref->fields);
+ 	Assert(IsA(field1, String));
+ 	pname = strVal(field1);
+ 
+ 	if (names == 3)
+ 	{
+ 		/*
+ 		 * Function-qualified reference: if the first name doesn't match
+ 		 * the function, we can fail immediately. Otherwise, discard the
+ 		 * first name, and continue.
+ 		 */
+ 		if (strcmp(pname, pinfo->name) != 0)
+ 			return NULL;
+ 
+ 		Assert(IsA(subfield, String));
+ 		pname = strVal(subfield);
+ 		param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location);
+ 		subfield = (Node *) lthird(cref->fields);
+ 	}
+ 	else
+ 	{
+ 		param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location);
+ 
+ 		if (!param && names == 2 && strcmp(pname, pinfo->name) == 0)
+ 		{
+ 			/*
+ 			 * We have a two-part name, the first part matches the name
+ 			 * of our containing function, and did not match a
+ 			 * parameter; discard the first name, and try again.
+ 			 */
+ 			Assert(IsA(subfield, String));
+ 			pname = strVal(subfield);
+ 			param = sql_fn_resolve_name(pstate, pinfo, pname, cref->location);
+ 			subfield = NULL;
+ 		}
+ 	}
+ 
+ 	if (!param)
+ 		return NULL;
+ 
+ 	if (subfield)
+ 	{
+ 		Assert(IsA(subfield, String));
+ 
+ 		param = ParseFuncOrColumn(pstate,
+ 								  list_make1(subfield),
+ 								  list_make1(param),
+ 								  NIL, false, false, false,
+ 								  NULL, true, cref->location);
+ 	}
+ 
+ 	return param;
+ }
+ 
  /*
   * sql_fn_param_ref		parser callback for ParamRefs ($n symbols)
   */
*************** sql_fn_param_ref(ParseState *pstate, Par
*** 226,231 ****
--- 357,372 ----
  {
  	SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
  	int			paramno = pref->number;
+ 
+ 	return sql_fn_param_ref_num(pstate, pinfo, paramno, pref->location);
+ }
+ 
+ /*
+  * sql_fn_param_ref_num		construct a Param node for the given paramno
+  */
+ static Node *
+ sql_fn_param_ref_num(ParseState *pstate, SQLFunctionParseInfoPtr pinfo, int paramno, int location)
+ {
  	Param	   *param;
  
  	/* Check parameter number is valid */
*************** sql_fn_param_ref(ParseState *pstate, Par
*** 238,244 ****
  	param->paramtype = pinfo->argtypes[paramno - 1];
  	param->paramtypmod = -1;
  	param->paramcollid = get_typcollation(param->paramtype);
! 	param->location = pref->location;
  
  	/*
  	 * If we have a function input collation, allow it to override the
--- 379,385 ----
  	param->paramtype = pinfo->argtypes[paramno - 1];
  	param->paramtypmod = -1;
  	param->paramcollid = get_typcollation(param->paramtype);
! 	param->location = location;
  
  	/*
  	 * If we have a function input collation, allow it to override the
diff --git a/src/test/regress/input/create_function_2.source b/src/test/regress/input/create_function_2.source
new file mode 100644
index 6aed5f0..3207870
*** a/src/test/regress/input/create_function_2.source
--- b/src/test/regress/input/create_function_2.source
*************** CREATE FUNCTION hobby_construct(text, te
*** 13,18 ****
--- 13,24 ----
     LANGUAGE SQL;
  
  
+ CREATE FUNCTION hobby_construct_named(name text, hobby text)
+    RETURNS hobbies_r
+    AS 'select name, hobby'
+    LANGUAGE SQL;
+ 
+ 
  CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
     RETURNS hobbies_r.person%TYPE
     AS 'select person from hobbies_r where name = $1'
*************** CREATE FUNCTION equipment(hobbies_r)
*** 25,30 ****
--- 31,42 ----
     LANGUAGE SQL;
  
  
+ CREATE FUNCTION equipment_named(hobby hobbies_r)
+    RETURNS setof equipment_r
+    AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name'
+    LANGUAGE SQL;
+ 
+ 
  CREATE FUNCTION user_relns()
     RETURNS setof name
     AS 'select relname
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source
new file mode 100644
index 0930a6a..958db67
*** a/src/test/regress/input/misc.source
--- b/src/test/regress/input/misc.source
*************** SELECT user_relns() AS user_relns
*** 217,222 ****
--- 217,226 ----
  
  SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
  
+ SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer')));
+ 
+ SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer')));
+ 
  SELECT hobbies_by_name('basketball');
  
  SELECT name, overpaid(emp.*) FROM emp;
diff --git a/src/test/regress/output/create_function_2.source b/src/test/regress/output/create_function_2.source
new file mode 100644
index 94ab7eb..e7ef281
*** a/src/test/regress/output/create_function_2.source
--- b/src/test/regress/output/create_function_2.source
*************** CREATE FUNCTION hobby_construct(text, te
*** 9,14 ****
--- 9,18 ----
     RETURNS hobbies_r
     AS 'select $1 as name, $2 as hobby'
     LANGUAGE SQL;
+ CREATE FUNCTION hobby_construct_named(name text, hobby text)
+    RETURNS hobbies_r
+    AS 'select name, hobby'
+    LANGUAGE SQL;
  CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
     RETURNS hobbies_r.person%TYPE
     AS 'select person from hobbies_r where name = $1'
*************** CREATE FUNCTION equipment(hobbies_r)
*** 19,24 ****
--- 23,32 ----
     RETURNS setof equipment_r
     AS 'select * from equipment_r where hobby = $1.name'
     LANGUAGE SQL;
+ CREATE FUNCTION equipment_named(hobby hobbies_r)
+    RETURNS setof equipment_r
+    AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name'
+    LANGUAGE SQL;
  CREATE FUNCTION user_relns()
     RETURNS setof name
     AS 'select relname
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source
new file mode 100644
index c225d0f..e1312e6
*** a/src/test/regress/output/misc.source
--- b/src/test/regress/output/misc.source
*************** SELECT name(equipment(hobby_construct(te
*** 677,682 ****
--- 677,694 ----
   guts
  (1 row)
  
+ SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer')));
+  name 
+ ------
+  guts
+ (1 row)
+ 
+ SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer')));
+  name 
+ ------
+  guts
+ (1 row)
+ 
  SELECT hobbies_by_name('basketball');
   hobbies_by_name 
  -----------------

