diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 069adf46af5acaf4085b02e8ec6cca0a2ee18ff9..c2c521bbfeab77ddc2e1ffbbbcc9bc240763c4e1 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.57 2005/03/29 00:16:57 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.58 2005/03/29 17:58:49 tgl Exp $
  *
  * DESCRIPTION
  *	  These routines take the parse tree and pick out the
@@ -154,6 +154,15 @@ examine_parameter_list(List *parameter, Oid languageOid,
 				   errmsg("functions cannot have more than %d arguments",
 						  FUNC_MAX_ARGS)));
 
+		if (fp->mode == FUNC_PARAM_OUT)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("CREATE FUNCTION / OUT parameters are not implemented")));
+		if (fp->mode == FUNC_PARAM_INOUT)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("CREATE FUNCTION / INOUT parameters are not implemented")));
+
 		toid = LookupTypeName(t);
 		if (OidIsValid(toid))
 		{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 92f7168ae9fbf4d3337636c41a5b5b056f71cfdb..c2130e370d4b8b8277725d131c617d1e11f9e40b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.298 2005/03/14 00:19:36 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.299 2005/03/29 17:58:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1888,6 +1888,7 @@ _copyFunctionParameter(FunctionParameter *from)
 
 	COPY_STRING_FIELD(name);
 	COPY_NODE_FIELD(argType);
+	COPY_SCALAR_FIELD(mode);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cbd99dab7207787f992efcaa810722296cee2d36..bcf8c36393725505e5a7ee600b577dd47d061326 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.237 2005/03/14 00:19:36 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.238 2005/03/29 17:58:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -949,6 +949,7 @@ _equalFunctionParameter(FunctionParameter *a, FunctionParameter *b)
 {
 	COMPARE_STRING_FIELD(name);
 	COMPARE_NODE_FIELD(argType);
+	COMPARE_SCALAR_FIELD(mode);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a88262d432816afb3755be874688a8bbb01dc225..62497957263e6c709d52bc1e83cd68894bedbffe 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.484 2005/03/14 00:19:36 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.485 2005/03/29 17:58:50 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -114,6 +114,8 @@ static void doNegateFloat(Value *v);
 
 	TypeName			*typnam;
 	FunctionParameter   *fun_param;
+	FunctionParameterMode fun_param_mode;
+	FuncWithArgs		*funwithargs;
 	DefElem				*defelt;
 	SortBy				*sortby;
 	JoinExpr			*jexpr;
@@ -206,7 +208,7 @@ static void doNegateFloat(Value *v);
 %type <ival>	privilege
 %type <list>	privileges privilege_list
 %type <privtarget> privilege_target
-%type <node>	function_with_argtypes
+%type <funwithargs> function_with_argtypes
 %type <list>	function_with_argtypes_list
 %type <chr> 	TriggerOneEvent
 
@@ -233,9 +235,10 @@ static void doNegateFloat(Value *v);
 
 %type <defelt>	createfunc_opt_item common_func_opt_item
 %type <fun_param> func_arg
+%type <fun_param_mode> arg_class
 %type <typnam>	func_return func_type aggr_argtype
 
-%type <boolean> arg_class TriggerForType OptTemp
+%type <boolean>  TriggerForType OptTemp
 %type <oncommit> OnCommitOption
 %type <withoids> OptWithOids WithOidsAs
 
@@ -3177,7 +3180,7 @@ function_with_argtypes:
 					FuncWithArgs *n = makeNode(FuncWithArgs);
 					n->funcname = $1;
 					n->funcargs = extractArgTypes($2);
-					$$ = (Node *)n;
+					$$ = n;
 				}
 		;
 
@@ -3295,7 +3298,13 @@ func_args_list:
 			| func_args_list ',' func_arg			{ $$ = lappend($1, $3); }
 		;
 
-/* We can catch over-specified arguments here if we want to,
+/*
+ * The style with arg_class first is SQL99 standard, but Oracle puts
+ * param_name first; accept both since it's likely people will try both
+ * anyway.  Don't bother trying to save productions by letting arg_class
+ * have an empty alternative ... you'll get shift/reduce conflicts.
+ *
+ * We can catch over-specified arguments here if we want to,
  * but for now better to silently swallow typmod, etc.
  * - thomas 2000-03-22
  */
@@ -3305,33 +3314,48 @@ func_arg:
 					FunctionParameter *n = makeNode(FunctionParameter);
 					n->name = $2;
 					n->argType = $3;
+					n->mode = $1;
 					$$ = n;
 				}
-			| arg_class func_type
+			| param_name arg_class func_type
 				{
 					FunctionParameter *n = makeNode(FunctionParameter);
-					n->name = NULL;
+					n->name = $1;
+					n->argType = $3;
+					n->mode = $2;
+					$$ = n;
+				}
+			| param_name func_type
+				{
+					FunctionParameter *n = makeNode(FunctionParameter);
+					n->name = $1;
 					n->argType = $2;
+					n->mode = FUNC_PARAM_IN;
 					$$ = n;
 				}
-		;
-
-arg_class:	IN_P									{ $$ = FALSE; }
-			| OUT_P
+			| arg_class func_type
 				{
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("CREATE FUNCTION / OUT parameters are not implemented")));
-					$$ = TRUE;
+					FunctionParameter *n = makeNode(FunctionParameter);
+					n->name = NULL;
+					n->argType = $2;
+					n->mode = $1;
+					$$ = n;
 				}
-			| INOUT
+			| func_type
 				{
-					ereport(ERROR,
-							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-							 errmsg("CREATE FUNCTION / INOUT parameters are not implemented")));
-					$$ = FALSE;
+					FunctionParameter *n = makeNode(FunctionParameter);
+					n->name = NULL;
+					n->argType = $1;
+					n->mode = FUNC_PARAM_IN;
+					$$ = n;
 				}
-			| /*EMPTY*/								{ $$ = FALSE; }
+		;
+
+/* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */
+arg_class:	IN_P									{ $$ = FUNC_PARAM_IN; }
+			| OUT_P									{ $$ = FUNC_PARAM_OUT; }
+			| INOUT									{ $$ = FUNC_PARAM_INOUT; }
+			| IN_P OUT_P							{ $$ = FUNC_PARAM_INOUT; }
 		;
 
 /*
@@ -3458,7 +3482,7 @@ AlterFunctionStmt:
 			ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict
 				{
 					AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
-					n->func = (FuncWithArgs *) $3;
+					n->func = $3;
 					n->actions = $4;
 					$$ = (Node *) n;
 				}
@@ -3561,7 +3585,7 @@ CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
 					CreateCastStmt *n = makeNode(CreateCastStmt);
 					n->sourcetype = $4;
 					n->targettype = $6;
-					n->func = (FuncWithArgs *) $10;
+					n->func = $10;
 					n->context = (CoercionContext) $11;
 					$$ = (Node *)n;
 				}
@@ -8299,9 +8323,9 @@ check_func_name(List *names)
 
 /* extractArgTypes()
  * Given a list of FunctionParameter nodes, extract a list of just the
- * argument types (TypeNames).  Most of the productions using func_args
- * don't currently want the full FunctionParameter data, so we use this
- * rather than having two sets of productions.
+ * argument types (TypeNames) for input parameters only.  This is what
+ * is needed to look up an existing function, which is what is wanted by
+ * the productions that use this call.
  */
 static List *
 extractArgTypes(List *parameters)
@@ -8313,7 +8337,8 @@ extractArgTypes(List *parameters)
 	{
 		FunctionParameter *p = (FunctionParameter *) lfirst(i);
 
-		result = lappend(result, p->argType);
+		if (p->mode != FUNC_PARAM_OUT)			/* keep if IN or INOUT */
+			result = lappend(result, p->argType);
 	}
 	return result;
 }
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8cf274b14ce1374a5151895b56d5f860247fcf48..a6a79e3a4fa5fc041bcdd3b9f9f8bb19915454cd 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.274 2005/03/14 00:19:37 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.275 2005/03/29 17:58:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -899,6 +899,11 @@ typedef struct PrivGrantee
 	char	   *groupname;
 } PrivGrantee;
 
+/*
+ * Note: FuncWithArgs carries only the types of the input parameters of the
+ * function.  So it is sufficient to identify an existing function, but it
+ * is not enough info to define a function nor to call it.
+ */
 typedef struct FuncWithArgs
 {
 	NodeTag		type;
@@ -1389,12 +1394,20 @@ typedef struct CreateFunctionStmt
 	List	   *withClause;		/* a list of DefElem */
 } CreateFunctionStmt;
 
+typedef enum FunctionParameterMode
+{
+	/* the assigned enum values appear in pg_proc, don't change 'em! */
+	FUNC_PARAM_IN = 'i',		/* input only */
+	FUNC_PARAM_OUT = 'o',		/* output only */
+	FUNC_PARAM_INOUT = 'b'		/* both */
+} FunctionParameterMode;
+
 typedef struct FunctionParameter
 {
 	NodeTag		type;
 	char	   *name;			/* parameter name, or NULL if not given */
 	TypeName   *argType;		/* TypeName for parameter type */
-	/* someday add IN/OUT/INOUT indicator here */
+	FunctionParameterMode mode;	/* IN/OUT/INOUT */
 } FunctionParameter;
 
 typedef struct AlterFunctionStmt