diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 789faad772c739468bd9feec0d0c147867da54f5..78bda61b30fc72e4e2e8f674ab5860e8d274dc68 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: outfuncs.c,v 1.98 1999/11/23 20:06:53 momjian Exp $
+ *	$Id: outfuncs.c,v 1.99 1999/12/10 07:37:31 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -114,8 +114,12 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
 static void
 _outFuncCall(StringInfo str, FuncCall *node)
 {
-	appendStringInfo(str, "FUNCTION %s :args ", stringStringInfo(node->funcname));
+	appendStringInfo(str, "FUNCTION %s :args ",
+					 stringStringInfo(node->funcname));
 	_outNode(str, node->args);
+	appendStringInfo(str, " :agg_star %s :agg_distinct %s ",
+					 node->agg_star ? "true" : "false",
+					 node->agg_distinct ? "true" : "false");
 }
 
 static void
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e93bd13d9a11c6842c64782f5c2224141494d1ed..2162bbff315c77b8121e422b38567be9b54be411 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: analyze.c,v 1.125 1999/12/06 18:02:42 wieck Exp $
+ *	$Id: analyze.c,v 1.126 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -624,6 +624,8 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
 					funccallnode = makeNode(FuncCall);
 					funccallnode->funcname = "nextval";
 					funccallnode->args = lcons(snamenode, NIL);
+					funccallnode->agg_star = false;
+					funccallnode->agg_distinct = false;
 
 					constraint = makeNode(Constraint);
 					constraint->contype = CONSTR_DEFAULT;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4352bb327a75ce9ead948e9232fccf57730c0d43..d3fb6291806aaa7652c05705cd0850a128b299aa 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.120 1999/12/10 05:17:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.121 1999/12/10 07:37:35 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -246,7 +246,7 @@ static Node *doNegate(Node *n);
 %type <str>		TypeId
 
 %type <node>	TableConstraint
-%type <list>	ColPrimaryKey, ColQualifier
+%type <list>	ColPrimaryKey, ColConstraintList
 %type <node>	ColConstraint, ColConstraintElem
 %type <ival>	key_actions, key_action, key_reference
 %type <str>		key_match
@@ -912,7 +912,7 @@ OptTableElement:  columnDef						{ $$ = $1; }
 			| TableConstraint					{ $$ = $1; }
 		;
 
-columnDef:  ColId Typename ColQualifier
+columnDef:  ColId Typename ColConstraintList
 				{
 					ColumnDef *n = makeNode(ColumnDef);
 					n->colname = $1;
@@ -939,14 +939,15 @@ columnDef:  ColId Typename ColQualifier
 				}
 		;
 
-ColQualifier:  ColQualifier ColConstraint
+ColConstraintList:  ColConstraintList ColConstraint
 				{
 					if ($2 != NULL)
 						$$ = lappend($1, $2);
 					else
 						$$ = $1;
 				}
-			| /*EMPTY*/							{ $$ = NULL; }
+			| /*EMPTY*/
+				{ $$ = NIL; }
 		;
 
 ColPrimaryKey:  PRIMARY KEY
@@ -3792,6 +3793,8 @@ a_expr:  com_expr
 						FuncCall *n = makeNode(FuncCall);
 						n->funcname = $3->name;
 						n->args = lcons($1,NIL);
+						n->agg_star = false;
+						n->agg_distinct = false;
 						$$ = (Node *)n;
 					}
 				}
@@ -4037,6 +4040,8 @@ b_expr:  com_expr
 						FuncCall *n = makeNode(FuncCall);
 						n->funcname = $3->name;
 						n->args = lcons($1,NIL);
+						n->agg_star = false;
+						n->agg_distinct = false;
 						$$ = (Node *)n;
 					}
 				}
@@ -4129,6 +4134,8 @@ com_expr:  attr
 						FuncCall *n = makeNode(FuncCall);
 						n->funcname = $5->name;
 						n->args = lcons($3,NIL);
+						n->agg_star = false;
+						n->agg_distinct = false;
 						$$ = (Node *)n;
 					}
 				}
@@ -4139,6 +4146,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = $1;
 					n->args = NIL;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| func_name '(' expr_list ')'
@@ -4146,6 +4155,17 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = $1;
 					n->args = $3;
+					n->agg_star = false;
+					n->agg_distinct = false;
+					$$ = (Node *)n;
+				}
+		| func_name '(' DISTINCT expr_list ')'
+				{
+					FuncCall *n = makeNode(FuncCall);
+					n->funcname = $1;
+					n->args = $4;
+					n->agg_star = false;
+					n->agg_distinct = true;
 					$$ = (Node *)n;
 				}
 		| func_name '(' '*' ')'
@@ -4158,12 +4178,9 @@ com_expr:  attr
 					 * and there are no other aggregates in SQL92 that accept
 					 * '*' as parameter.
 					 *
-					 * XXX really, the '*' ought to be transformed to some
-					 * special construct that wouldn't be acceptable as the
-					 * input of a non-aggregate function, in case the given
-					 * func_name matches a plain function.  This would also
-					 * support a possible extension to let user-defined
-					 * aggregates do something special with '*' as input.
+					 * The FuncCall node is also marked agg_star = true,
+					 * so that later processing can detect what the argument
+					 * really was.
 					 */
 					FuncCall *n = makeNode(FuncCall);
 					A_Const *star = makeNode(A_Const);
@@ -4172,6 +4189,8 @@ com_expr:  attr
 					star->val.val.ival = 1;
 					n->funcname = $1;
 					n->args = lcons(star, NIL);
+					n->agg_star = true;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| CURRENT_DATE
@@ -4203,6 +4222,8 @@ com_expr:  attr
 
 					n->funcname = xlateSqlType("date");
 					n->args = lcons(s, NIL);
+					n->agg_star = false;
+					n->agg_distinct = false;
 
 					$$ = (Node *)n;
 				}
@@ -4226,6 +4247,8 @@ com_expr:  attr
 
 					n->funcname = xlateSqlType("time");
 					n->args = lcons(s, NIL);
+					n->agg_star = false;
+					n->agg_distinct = false;
 
 					$$ = (Node *)n;
 				}
@@ -4249,6 +4272,8 @@ com_expr:  attr
 
 					n->funcname = xlateSqlType("time");
 					n->args = lcons(s, NIL);
+					n->agg_star = false;
+					n->agg_distinct = false;
 
 					if ($3 != 0)
 						elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
@@ -4275,6 +4300,8 @@ com_expr:  attr
 
 					n->funcname = xlateSqlType("timestamp");
 					n->args = lcons(s, NIL);
+					n->agg_star = false;
+					n->agg_distinct = false;
 
 					$$ = (Node *)n;
 				}
@@ -4298,6 +4325,8 @@ com_expr:  attr
 
 					n->funcname = xlateSqlType("timestamp");
 					n->args = lcons(s, NIL);
+					n->agg_star = false;
+					n->agg_distinct = false;
 
 					if ($3 != 0)
 						elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
@@ -4309,6 +4338,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "getpgusername";
 					n->args = NIL;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| USER
@@ -4316,6 +4347,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "getpgusername";
 					n->args = NIL;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| EXTRACT '(' extract_list ')'
@@ -4323,6 +4356,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "date_part";
 					n->args = $3;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| POSITION '(' position_list ')'
@@ -4330,6 +4365,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "strpos";
 					n->args = $3;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| SUBSTRING '(' substr_list ')'
@@ -4337,6 +4374,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "substr";
 					n->args = $3;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		/* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
@@ -4345,6 +4384,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "btrim";
 					n->args = $4;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| TRIM '(' LEADING trim_list ')'
@@ -4352,6 +4393,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "ltrim";
 					n->args = $4;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| TRIM '(' TRAILING trim_list ')'
@@ -4359,6 +4402,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "rtrim";
 					n->args = $4;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| TRIM '(' trim_list ')'
@@ -4366,6 +4411,8 @@ com_expr:  attr
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = "btrim";
 					n->args = $3;
+					n->agg_star = false;
+					n->agg_distinct = false;
 					$$ = (Node *)n;
 				}
 		| '(' SubSelect ')'
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 4e30e60ca65a7b01b11ae5c2fa69c0066f7e1674..68280f7f4a0a9ea8d75ce25951921d4fdb0428c6 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.30 1999/12/09 05:58:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.31 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -203,7 +203,8 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
 
 Aggref *
 ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
-		 List *target, int precedence)
+		 List *args, bool agg_star, bool agg_distinct,
+		 int precedence)
 {
 	HeapTuple	theAggTuple;
 	Form_pg_aggregate aggform;
@@ -242,7 +243,7 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
 	if (OidIsValid(xfn1))
 	{
 		basetype = aggform->aggbasetype;
-		vartype = exprType(lfirst(target));
+		vartype = exprType(lfirst(args));
 		if ((basetype != vartype)
 			&& (!IS_BINARY_COMPATIBLE(basetype, vartype)))
 		{
@@ -261,9 +262,17 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
 	aggref->aggname = pstrdup(aggname);
 	aggref->basetype = aggform->aggbasetype;
 	aggref->aggtype = fintype;
-	aggref->target = lfirst(target);
+	aggref->target = lfirst(args);
 	aggref->usenulls = usenulls;
 
+	/*
+	 * We should store agg_star and agg_distinct into the Aggref node,
+	 * and let downstream processing deal with them.  Currently, agg_star
+	 * is ignored and agg_distinct is not implemented...
+	 */
+	if (agg_distinct)
+		elog(ERROR, "aggregate(DISTINCT ...) is not implemented yet");
+
 	pstate->p_hasAggs = true;
 
 	return aggref;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 0d51f2ae0565ebb1bd718a76a2021fad5b463d8c..95315d6c8244c17a6c7163269f9818b7fe5d088c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.25 1999/11/22 17:56:20 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.26 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,6 +99,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId,
 
 		n->funcname = typeTypeName(targetType);
 		n->args = lcons(node, NIL);
+		n->agg_star = false;
+		n->agg_distinct = false;
 
 		result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
 
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 76e5f91f1a8978247d8e92f44c585196149a3e72..ee43be4195068c7c5e0e483974ac3cddc8731016 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.59 1999/11/15 02:00:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.60 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,8 +106,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							Node	   *lexpr = transformExpr(pstate, a->lexpr, precedence);
 
 							result = ParseFuncOrColumn(pstate,
-										  "nullvalue", lcons(lexpr, NIL),
-												   &pstate->p_last_resno,
+													   "nullvalue",
+													   lcons(lexpr, NIL),
+													   false, false,
+													   &pstate->p_last_resno,
 													   precedence);
 						}
 						break;
@@ -116,8 +118,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 							Node	   *lexpr = transformExpr(pstate, a->lexpr, precedence);
 
 							result = ParseFuncOrColumn(pstate,
-									   "nonnullvalue", lcons(lexpr, NIL),
-												   &pstate->p_last_resno,
+													   "nonnullvalue",
+													   lcons(lexpr, NIL),
+													   false, false,
+													   &pstate->p_last_resno,
 													   precedence);
 						}
 						break;
@@ -192,6 +196,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 				result = ParseFuncOrColumn(pstate,
 										   fn->funcname,
 										   fn->args,
+										   fn->agg_star,
+										   fn->agg_distinct,
 										   &pstate->p_last_resno,
 										   precedence);
 				break;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index aafa4e4c9cb057a25113e61b34fa1f2a3b05656b..42143746b815c77cfa5c7964eaa01e439af49308 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.63 1999/12/07 04:09:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.64 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,6 +87,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
 
 		retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
 								   lcons(param, NIL),
+								   false, false,
 								   curr_resno,
 								   precedence);
 	}
@@ -98,6 +99,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
 		ident->isRel = TRUE;
 		retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
 								   lcons(ident, NIL),
+								   false, false,
 								   curr_resno,
 								   precedence);
 	}
@@ -107,6 +109,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
 	{
 		retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
 								   lcons(retval, NIL),
+								   false, false,
 								   curr_resno,
 								   precedence);
 	}
@@ -219,6 +222,7 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
+				  bool agg_star, bool agg_distinct,
 				  int *curr_resno, int precedence)
 {
 	Oid			rettype = InvalidOid;
@@ -230,12 +234,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 	char	   *refname = NULL;
 	Relation	rd;
 	Oid			relid;
-	int			nargs;
+	int			nargs = length(fargs);
 	Func	   *funcnode;
 	Oid			oid_array[MAXFARGS];
 	Oid		   *true_oid_array;
 	Node	   *retval;
 	bool		retset;
+	bool		must_be_agg = agg_star || agg_distinct;
 	bool		attisset = false;
 	Oid			toid = InvalidOid;
 	Expr	   *expr;
@@ -252,11 +257,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 	 * that argument is a relation, param, or PQ function returning a
 	 * complex * type, then the function could be a projection.
 	 */
-	/* We only have one parameter */
-	if (length(fargs) == 1)
+	/* We only have one parameter, and it's not got aggregate decoration */
+	if (nargs == 1 && !must_be_agg)
 	{
-		/* Is is a plain Relation name from the parser? */
-		if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel)
+		/* Is it a plain Relation name from the parser? */
+		if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
 		{
 			RangeTblEntry *rte;
 			Ident	   *ident = (Ident *) first_arg;
@@ -292,15 +297,10 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 										 refname,
 										 funcname);
 			}
-			else
-			{
-				/* drop through - attr is a set */
-				;
-			}
+			/* else drop through - attr is a set */
 		}
 		else if (ISCOMPLEX(exprType(first_arg)))
 		{
-
 			/*
 			 * Attempt to handle projection of a complex argument. If
 			 * ParseComplexProjection can't handle the projection, we have
@@ -325,8 +325,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 				argrelid = typeidTypeRelid(toid);
 
 				/*
-				 * A projection contains either an attribute name or the
-				 * "*".
+				 * A projection contains either an attribute name or "*".
 				 */
 				if ((get_attnum(argrelid, funcname) == InvalidAttrNumber)
 					&& strcmp(funcname, "*"))
@@ -336,76 +335,99 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 			if (retval)
 				return retval;
 		}
-		else
-		{
+	}
 
-			/*
-			 * Parsing aggregates.
-			 */
-			Type		tp;
-			Oid			basetype;
-			int			ncandidates;
-			CandidateList candidates;
+	if (nargs == 1 || must_be_agg)
+	{
+		/*
+		 * See if it's an aggregate.
+		 */
+		Oid			basetype;
+		int			ncandidates;
+		CandidateList candidates;
 
-			/*
-			 * the aggregate COUNT is a special case, ignore its base
-			 * type.  Treat it as zero
-			 */
-			if (strcmp(funcname, "count") == 0)
-				basetype = 0;
-			else
-				basetype = exprType(lfirst(fargs));
+		/* We don't presently cope with, eg, foo(DISTINCT x,y) */
+		if (nargs != 1)
+			elog(ERROR, "Aggregate functions may only have one parameter");
 
-			/* try for exact match first... */
-			if (SearchSysCacheTuple(AGGNAME,
-									PointerGetDatum(funcname),
-									ObjectIdGetDatum(basetype),
-									0, 0))
-				return (Node *) ParseAgg(pstate, funcname, basetype,
-										 fargs, precedence);
+		/*
+		 * the aggregate COUNT is a special case, ignore its base
+		 * type.  Treat it as zero.   XXX mighty ugly --- FIXME
+		 */
+		if (strcmp(funcname, "count") == 0)
+			basetype = 0;
+		else
+			basetype = exprType(lfirst(fargs));
 
-			/*
-			 * No exact match yet, so see if there is another entry in the
-			 * aggregate table which is compatible. - thomas 1998-12-05
-			 */
-			ncandidates = agg_get_candidates(funcname, basetype, &candidates);
-			if (ncandidates > 0)
-			{
-				Oid			type;
+		/* try for exact match first... */
+		if (SearchSysCacheTuple(AGGNAME,
+								PointerGetDatum(funcname),
+								ObjectIdGetDatum(basetype),
+								0, 0))
+			return (Node *) ParseAgg(pstate, funcname, basetype,
+									 fargs, agg_star, agg_distinct,
+									 precedence);
 
-				type = agg_select_candidate(basetype, candidates);
-				if (OidIsValid(type))
-				{
-					lfirst(fargs) = coerce_type(pstate, lfirst(fargs),
-												basetype, type, -1);
-					basetype = type;
+		/*
+		 * No exact match yet, so see if there is another entry in the
+		 * aggregate table which is compatible. - thomas 1998-12-05
+		 */
+		ncandidates = agg_get_candidates(funcname, basetype, &candidates);
+		if (ncandidates > 0)
+		{
+			Oid			type;
 
-					return (Node *) ParseAgg(pstate, funcname, basetype,
-											 fargs, precedence);
-				}
-				else
-				{
-					elog(ERROR, "Unable to select an aggregate function %s(%s)",
-						 funcname, typeidTypeName(basetype));
-				}
+			type = agg_select_candidate(basetype, candidates);
+			if (OidIsValid(type))
+			{
+				lfirst(fargs) = coerce_type(pstate, lfirst(fargs),
+											basetype, type, -1);
+				basetype = type;
+				return (Node *) ParseAgg(pstate, funcname, basetype,
+										 fargs, agg_star, agg_distinct,
+										 precedence);
+			}
+			else
+			{
+				/* Multiple possible matches --- give up */
+				elog(ERROR, "Unable to select an aggregate function %s(%s)",
+					 funcname, typeidTypeName(basetype));
 			}
+		}
 
+		if (must_be_agg)
+		{
 			/*
-			 * See if this is a single argument function with the function
-			 * name also a type name and the input argument and type name
-			 * binary compatible... This means that you are trying for a
-			 * type conversion which does not need to take place, so we'll
-			 * just pass through the argument itself. (make this clearer
-			 * with some extra brackets - thomas 1998-12-05)
+			 * No matching agg, but we had '*' or DISTINCT, so a plain
+			 * function could not have been meant.
 			 */
-			if ((HeapTupleIsValid(tp = SearchSysCacheTuple(TYPENAME,
-											   PointerGetDatum(funcname),
-														   0, 0, 0)))
-				&& IS_BINARY_COMPATIBLE(typeTypeId(tp), basetype))
-				return ((Node *) lfirst(fargs));
+			elog(ERROR, "There is no aggregate function %s(%s)",
+				 funcname, typeidTypeName(basetype));
 		}
 	}
 
+	/*
+	 * See if this is a single argument function with the function
+	 * name also a type name and the input argument and type name
+	 * binary compatible... This means that you are trying for a
+	 * type conversion which does not need to take place, so we'll
+	 * just pass through the argument itself. (make this clearer
+	 * with some extra brackets - thomas 1998-12-05)
+	 */
+	if (nargs == 1)
+	{
+		Type		tp;
+
+		tp = SearchSysCacheTuple(TYPENAME,
+								 PointerGetDatum(funcname),
+								 0, 0, 0);
+		if (HeapTupleIsValid(tp) &&
+			IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs))))
+		{
+			/* XXX FIXME: probably need to change expression's marked type? */
+			return (Node *) lfirst(fargs);
+		}
+	}
 
 	/*
 	 * If we dropped through to here it's really a function (or a set,
@@ -461,14 +483,14 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 		{						/* set functions don't have parameters */
 
 			/*
-			 * any functiona args which are typed "unknown", but aren't
+			 * any function args which are typed "unknown", but aren't
 			 * constants, we don't know what to do with, because we can't
 			 * cast them	- jolly
 			 */
 			if (exprType(pair) == UNKNOWNOID && !IsA(pair, Const))
 				elog(ERROR, "There is no function '%s'"
 					 " with argument #%d of type UNKNOWN",
-					 funcname, nargs);
+					 funcname, nargs+1);
 			else
 				toid = exprType(pair);
 		}
@@ -572,7 +594,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 		text	   *seqname;
 		int32		aclcheck_result = -1;
 
-		Assert(length(fargs) == ((funcid == F_SETVAL) ? 2 : 1));
+		Assert(nargs == ((funcid == F_SETVAL) ? 2 : 1));
 		seq = (Const *) lfirst(fargs);
 		if (!IsA((Node *) seq, Const))
 			elog(ERROR, "Only constant sequence names are acceptable for function '%s'", funcname);
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 8cac8b417cdc9b29d4ae1580fbb3243cee8d93fc..a5e130b85431c90be6a4894dcb928a1bf87ff18f 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.49 1999/11/22 17:56:21 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.50 1999/12/10 07:37:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -337,16 +337,16 @@ SizeTargetExpr(ParseState *pstate,
 
 	if (HeapTupleIsValid(ftup))
 	{
-		FuncCall   *func;
-		A_Const    *cons;
+		A_Const    *cons = makeNode(A_Const);
+		FuncCall   *func = makeNode(FuncCall);
 
-		func = makeNode(FuncCall);
-		func->funcname = funcname;
-
-		cons = makeNode(A_Const);
 		cons->val.type = T_Integer;
 		cons->val.val.ival = attrtypmod;
+
+		func->funcname = funcname;
 		func->args = lappend(lcons(expr, NIL), cons);
+		func->agg_star = false;
+		func->agg_distinct = false;
 
 		expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
 	}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bcff7aa30066fe23b813c8570eb1ee6dcc0d55e8..df0cb5c4e54cacbc597d4530b723e9f6ce0c5b32 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.89 1999/12/10 03:56:09 momjian Exp $
+ * $Id: parsenodes.h,v 1.90 1999/12/10 07:37:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -758,6 +758,10 @@ typedef struct SelectStmt
 
 /****************************************************************************
  *	Supporting data structures for Parse Trees
+ *
+ *	Most of these node types appear in raw parsetrees output by the grammar,
+ *	and get transformed to something else by the analyzer.  A few of them
+ *	are used as-is in transformed querytrees.
  ****************************************************************************/
 
 /*
@@ -889,13 +893,20 @@ typedef struct Ident
 } Ident;
 
 /*
- * FuncCall - a function/aggregate invocation
+ * FuncCall - a function or aggregate invocation
+ *
+ * agg_star indicates we saw a 'foo(*)' construct, while agg_distinct
+ * indicates we saw 'foo(DISTINCT ...)'.  In either case, the construct
+ * *must* be an aggregate call.  Otherwise, it might be either an
+ * aggregate or some other kind of function.
  */
 typedef struct FuncCall
 {
 	NodeTag		type;
 	char	   *funcname;		/* name of function */
 	List	   *args;			/* the arguments (list of exprs) */
+	bool		agg_star;		/* argument was really '*' */
+	bool		agg_distinct;	/* arguments were labeled DISTINCT */
 } FuncCall;
 
 /*
diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h
index f1b400e9c9ac5df7590943ab991c60e0edea0c9a..cd149e1517b81898e5a08acc4738a71ec432a593 100644
--- a/src/include/parser/parse_agg.h
+++ b/src/include/parser/parse_agg.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_agg.h,v 1.12 1999/07/15 23:04:01 momjian Exp $
+ * $Id: parse_agg.h,v 1.13 1999/12/10 07:37:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,8 @@
 extern void AddAggToParseState(ParseState *pstate, Aggref *aggref);
 extern void parseCheckAggregates(ParseState *pstate, Query *qry);
 extern Aggref *ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
-		 List *target, int precedence);
+						List *args, bool agg_star, bool agg_distinct,
+						int precedence);
 extern void agg_error(char *caller, char *aggname, Oid basetypeID);
 
 #endif	 /* PARSE_AGG_H */
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 1fdb8f9890da004a3da3b2283b0f2e4b91bb4715..04b9c44ec0e1890c3043f2b0f2ea928472ce7778 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.19 1999/08/21 03:49:17 tgl Exp $
+ * $Id: parse_func.h,v 1.20 1999/12/10 07:37:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,8 +42,10 @@ typedef struct _CandidateList
 
 extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
 						int *curr_resno, int precedence);
-extern Node *ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
-				  int *curr_resno, int precedence);
+extern Node *ParseFuncOrColumn(ParseState *pstate,
+							   char *funcname, List *fargs,
+							   bool agg_star, bool agg_distinct,
+							   int *curr_resno, int precedence);
 
 extern List *setup_base_tlist(Oid typeid);