diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 8572d8543a..736d04780a 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -227,11 +227,16 @@ interpret_function_parameter_list(ParseState *pstate,
 	{
 		FunctionParameter *fp = (FunctionParameter *) lfirst(x);
 		TypeName   *t = fp->argType;
+		FunctionParameterMode fpmode = fp->mode;
 		bool		isinput = false;
 		Oid			toid;
 		Type		typtup;
 		AclResult	aclresult;
 
+		/* For our purposes here, a defaulted mode spec is identical to IN */
+		if (fpmode == FUNC_PARAM_DEFAULT)
+			fpmode = FUNC_PARAM_IN;
+
 		typtup = LookupTypeName(NULL, t, NULL, false);
 		if (typtup)
 		{
@@ -288,7 +293,7 @@ interpret_function_parameter_list(ParseState *pstate,
 		}
 
 		/* handle input parameters */
-		if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
+		if (fpmode != FUNC_PARAM_OUT && fpmode != FUNC_PARAM_TABLE)
 		{
 			/* other input parameters can't follow a VARIADIC parameter */
 			if (varCount > 0)
@@ -302,7 +307,7 @@ interpret_function_parameter_list(ParseState *pstate,
 		}
 
 		/* handle output parameters */
-		if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
+		if (fpmode != FUNC_PARAM_IN && fpmode != FUNC_PARAM_VARIADIC)
 		{
 			if (objtype == OBJECT_PROCEDURE)
 			{
@@ -323,7 +328,7 @@ interpret_function_parameter_list(ParseState *pstate,
 			outCount++;
 		}
 
-		if (fp->mode == FUNC_PARAM_VARIADIC)
+		if (fpmode == FUNC_PARAM_VARIADIC)
 		{
 			*variadicArgType = toid;
 			varCount++;
@@ -346,7 +351,7 @@ interpret_function_parameter_list(ParseState *pstate,
 
 		allTypes[i] = ObjectIdGetDatum(toid);
 
-		paramModes[i] = CharGetDatum(fp->mode);
+		paramModes[i] = CharGetDatum(fpmode);
 
 		if (fp->name && fp->name[0])
 		{
@@ -361,19 +366,24 @@ interpret_function_parameter_list(ParseState *pstate,
 			foreach(px, parameters)
 			{
 				FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
+				FunctionParameterMode prevfpmode;
 
 				if (prevfp == fp)
 					break;
+				/* as above, default mode is IN */
+				prevfpmode = prevfp->mode;
+				if (prevfpmode == FUNC_PARAM_DEFAULT)
+					prevfpmode = FUNC_PARAM_IN;
 				/* pure in doesn't conflict with pure out */
-				if ((fp->mode == FUNC_PARAM_IN ||
-					 fp->mode == FUNC_PARAM_VARIADIC) &&
-					(prevfp->mode == FUNC_PARAM_OUT ||
-					 prevfp->mode == FUNC_PARAM_TABLE))
+				if ((fpmode == FUNC_PARAM_IN ||
+					 fpmode == FUNC_PARAM_VARIADIC) &&
+					(prevfpmode == FUNC_PARAM_OUT ||
+					 prevfpmode == FUNC_PARAM_TABLE))
 					continue;
-				if ((prevfp->mode == FUNC_PARAM_IN ||
-					 prevfp->mode == FUNC_PARAM_VARIADIC) &&
-					(fp->mode == FUNC_PARAM_OUT ||
-					 fp->mode == FUNC_PARAM_TABLE))
+				if ((prevfpmode == FUNC_PARAM_IN ||
+					 prevfpmode == FUNC_PARAM_VARIADIC) &&
+					(fpmode == FUNC_PARAM_OUT ||
+					 fpmode == FUNC_PARAM_TABLE))
 					continue;
 				if (prevfp->name && prevfp->name[0] &&
 					strcmp(prevfp->name, fp->name) == 0)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3dd467e0e9..92d541c7fb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3407,6 +3407,7 @@ _copyObjectWithArgs(const ObjectWithArgs *from)
 
 	COPY_NODE_FIELD(objname);
 	COPY_NODE_FIELD(objargs);
+	COPY_NODE_FIELD(objfuncargs);
 	COPY_SCALAR_FIELD(args_unspecified);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d38cf55e6d..f772f05c72 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1182,6 +1182,7 @@ _equalObjectWithArgs(const ObjectWithArgs *a, const ObjectWithArgs *b)
 {
 	COMPARE_NODE_FIELD(objname);
 	COMPARE_NODE_FIELD(objargs);
+	COMPARE_NODE_FIELD(objfuncargs);
 	COMPARE_SCALAR_FIELD(args_unspecified);
 
 	return true;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2bd09b1575..284f6b1978 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -7562,6 +7562,7 @@ function_with_argtypes:
 					ObjectWithArgs *n = makeNode(ObjectWithArgs);
 					n->objname = $1;
 					n->objargs = extractArgTypes($2);
+					n->objfuncargs = $2;
 					$$ = n;
 				}
 			/*
@@ -7642,7 +7643,7 @@ func_arg:
 					FunctionParameter *n = makeNode(FunctionParameter);
 					n->name = $1;
 					n->argType = $2;
-					n->mode = FUNC_PARAM_IN;
+					n->mode = FUNC_PARAM_DEFAULT;
 					n->defexpr = NULL;
 					$$ = n;
 				}
@@ -7660,7 +7661,7 @@ func_arg:
 					FunctionParameter *n = makeNode(FunctionParameter);
 					n->name = NULL;
 					n->argType = $1;
-					n->mode = FUNC_PARAM_IN;
+					n->mode = FUNC_PARAM_DEFAULT;
 					n->defexpr = NULL;
 					$$ = n;
 				}
@@ -7732,7 +7733,8 @@ func_arg_with_default:
 /* Aggregate args can be most things that function args can be */
 aggr_arg:	func_arg
 				{
-					if (!($1->mode == FUNC_PARAM_IN ||
+					if (!($1->mode == FUNC_PARAM_DEFAULT ||
+						  $1->mode == FUNC_PARAM_IN ||
 						  $1->mode == FUNC_PARAM_VARIADIC))
 						ereport(ERROR,
 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -7801,6 +7803,7 @@ aggregate_with_argtypes:
 					ObjectWithArgs *n = makeNode(ObjectWithArgs);
 					n->objname = $1;
 					n->objargs = extractAggrArgTypes($2);
+					n->objfuncargs = (List *) linitial($2);
 					$$ = n;
 				}
 		;
@@ -16987,7 +16990,9 @@ mergeTableFuncParameters(List *func_args, List *columns)
 	{
 		FunctionParameter *p = (FunctionParameter *) lfirst(lc);
 
-		if (p->mode != FUNC_PARAM_IN && p->mode != FUNC_PARAM_VARIADIC)
+		if (p->mode != FUNC_PARAM_DEFAULT &&
+			p->mode != FUNC_PARAM_IN &&
+			p->mode != FUNC_PARAM_VARIADIC)
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index eae78fea8f..e9b456dd85 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -50,7 +50,9 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
 									Node *first_arg, int location);
 static Oid	LookupFuncNameInternal(List *funcname, int nargs,
 								   const Oid *argtypes,
-								   bool missing_ok, FuncLookupError *lookupError);
+								   ObjectType objtype,
+								   bool include_out_arguments, bool missing_ok,
+								   FuncLookupError *lookupError);
 
 
 /*
@@ -2044,12 +2046,15 @@ func_signature_string(List *funcname, int nargs,
  *
  * Possible errors:
  *	FUNCLOOKUP_NOSUCHFUNC: we can't find a function of this name.
- *	FUNCLOOKUP_AMBIGUOUS: nargs == -1 and more than one function matches.
+ *	FUNCLOOKUP_AMBIGUOUS: more than one function matches.
  */
 static Oid
 LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
-					   bool missing_ok, FuncLookupError *lookupError)
+					   ObjectType objtype,
+					   bool include_out_arguments, bool missing_ok,
+					   FuncLookupError *lookupError)
 {
+	Oid			result = InvalidOid;
 	FuncCandidateList clist;
 
 	/* NULL argtypes allowed for nullary functions only */
@@ -2058,43 +2063,62 @@ LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
 	/* Always set *lookupError, to forestall uninitialized-variable warnings */
 	*lookupError = FUNCLOOKUP_NOSUCHFUNC;
 
+	/* Get list of candidate objects */
 	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false,
-								  false, missing_ok);
+								  include_out_arguments, missing_ok);
 
-	/*
-	 * If no arguments were specified, the name must yield a unique candidate.
-	 */
-	if (nargs < 0)
+	/* Scan list for a match to the arg types (if specified) and the objtype */
+	for (; clist != NULL; clist = clist->next)
 	{
-		if (clist)
+		/* Check arg type match, if specified */
+		if (nargs >= 0)
 		{
-			/* If there is a second match then it's ambiguous */
-			if (clist->next)
-			{
-				*lookupError = FUNCLOOKUP_AMBIGUOUS;
-				return InvalidOid;
-			}
-			/* Otherwise return the match */
-			return clist->oid;
+			/* if nargs==0, argtypes can be null; don't pass that to memcmp */
+			if (nargs > 0 &&
+				memcmp(argtypes, clist->args, nargs * sizeof(Oid)) != 0)
+				continue;
 		}
-		else
+
+		/* Check for duplicates reported by FuncnameGetCandidates */
+		if (!OidIsValid(clist->oid))
+		{
+			*lookupError = FUNCLOOKUP_AMBIGUOUS;
 			return InvalidOid;
-	}
+		}
 
-	/*
-	 * Otherwise, look for a match to the arg types.  FuncnameGetCandidates
-	 * has ensured that there's at most one match in the returned list.
-	 */
-	while (clist)
-	{
-		/* if nargs==0, argtypes can be null; don't pass that to memcmp */
-		if (nargs == 0 ||
-			memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0)
-			return clist->oid;
-		clist = clist->next;
+		/* Check objtype match, if specified */
+		switch (objtype)
+		{
+			case OBJECT_FUNCTION:
+			case OBJECT_AGGREGATE:
+				/* Ignore procedures */
+				if (get_func_prokind(clist->oid) == PROKIND_PROCEDURE)
+					continue;
+				break;
+			case OBJECT_PROCEDURE:
+				/* Ignore non-procedures */
+				if (get_func_prokind(clist->oid) != PROKIND_PROCEDURE)
+					continue;
+				break;
+			case OBJECT_ROUTINE:
+				/* no restriction */
+				break;
+			default:
+				Assert(false);
+		}
+
+		/* Check for multiple matches */
+		if (OidIsValid(result))
+		{
+			*lookupError = FUNCLOOKUP_AMBIGUOUS;
+			return InvalidOid;
+		}
+
+		/* OK, we have a candidate */
+		result = clist->oid;
 	}
 
-	return InvalidOid;
+	return result;
 }
 
 /*
@@ -2121,7 +2145,9 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
 	Oid			funcoid;
 	FuncLookupError lookupError;
 
-	funcoid = LookupFuncNameInternal(funcname, nargs, argtypes, missing_ok,
+	funcoid = LookupFuncNameInternal(funcname, nargs, argtypes,
+									 OBJECT_ROUTINE,	/* XXX okay? */
+									 false, missing_ok,
 									 &lookupError);
 
 	if (OidIsValid(funcoid))
@@ -2210,10 +2236,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
 								   FUNC_MAX_ARGS)));
 	}
 
+	/*
+	 * First, perform a lookup considering only input arguments (traditional
+	 * Postgres rules).
+	 */
 	i = 0;
 	foreach(args_item, func->objargs)
 	{
-		TypeName   *t = (TypeName *) lfirst(args_item);
+		TypeName   *t = lfirst_node(TypeName, args_item);
 
 		argoids[i] = LookupTypeNameOid(NULL, t, missing_ok);
 		if (!OidIsValid(argoids[i]))
@@ -2227,9 +2257,82 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
 	 */
 	nargs = func->args_unspecified ? -1 : argcount;
 
-	oid = LookupFuncNameInternal(func->objname, nargs, argoids, missing_ok,
+	/*
+	 * In args_unspecified mode, also tell LookupFuncNameInternal to consider
+	 * the object type, since there seems no reason not to.  However, if we
+	 * have an argument list, disable the objtype check, because we'd rather
+	 * complain about "object is of wrong type" than "object doesn't exist".
+	 * (Note that with args, FuncnameGetCandidates will have ensured there's
+	 * only one argtype match, so we're not risking an ambiguity failure via
+	 * this choice.)
+	 */
+	oid = LookupFuncNameInternal(func->objname, nargs, argoids,
+								 func->args_unspecified ? objtype : OBJECT_ROUTINE,
+								 false, missing_ok,
 								 &lookupError);
 
+	/*
+	 * If PROCEDURE or ROUTINE was specified, and we have an argument list
+	 * that contains no parameter mode markers, and we didn't already discover
+	 * that there's ambiguity, perform a lookup considering all arguments.
+	 * (Note: for a zero-argument procedure, or in args_unspecified mode, the
+	 * normal lookup is sufficient; so it's OK to require non-NIL objfuncargs
+	 * to perform this lookup.)
+	 */
+	if ((objtype == OBJECT_PROCEDURE || objtype == OBJECT_ROUTINE) &&
+		func->objfuncargs != NIL &&
+		lookupError != FUNCLOOKUP_AMBIGUOUS)
+	{
+		bool		have_param_mode = false;
+
+		/*
+		 * Check for non-default parameter mode markers.  If there are any,
+		 * then the command does not conform to SQL-spec syntax, so we may
+		 * assume that the traditional Postgres lookup method of considering
+		 * only input parameters is sufficient.  (Note that because the spec
+		 * doesn't have OUT arguments for functions, we also don't need this
+		 * hack in FUNCTION or AGGREGATE mode.)
+		 */
+		foreach(args_item, func->objfuncargs)
+		{
+			FunctionParameter *fp = lfirst_node(FunctionParameter, args_item);
+
+			if (fp->mode != FUNC_PARAM_DEFAULT)
+			{
+				have_param_mode = true;
+				break;
+			}
+		}
+
+		if (!have_param_mode)
+		{
+			Oid			poid;
+
+			/* Without mode marks, objargs surely includes all params */
+			Assert(list_length(func->objfuncargs) == argcount);
+
+			/* For objtype == OBJECT_PROCEDURE, we can ignore non-procedures */
+			poid = LookupFuncNameInternal(func->objname, argcount, argoids,
+										  objtype, true, missing_ok,
+										  &lookupError);
+
+			/* Combine results, handling ambiguity */
+			if (OidIsValid(poid))
+			{
+				if (OidIsValid(oid) && oid != poid)
+				{
+					/* oops, we got hits both ways, on different objects */
+					oid = InvalidOid;
+					lookupError = FUNCLOOKUP_AMBIGUOUS;
+				}
+				else
+					oid = poid;
+			}
+			else if (lookupError == FUNCLOOKUP_AMBIGUOUS)
+				oid = InvalidOid;
+		}
+	}
+
 	if (OidIsValid(oid))
 	{
 		/*
@@ -2238,6 +2341,10 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
 		 * we allow the objtype of FUNCTION to include aggregates and window
 		 * functions; but we draw the line if the object is a procedure.  That
 		 * is a new enough feature that this historical rule does not apply.
+		 *
+		 * (This check is partially redundant with the objtype check in
+		 * LookupFuncNameInternal; but not entirely, since we often don't tell
+		 * LookupFuncNameInternal to apply that check at all.)
 		 */
 		switch (objtype)
 		{
@@ -2348,28 +2455,32 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
 								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
 								 errmsg("function name \"%s\" is not unique",
 										NameListToString(func->objname)),
-								 errhint("Specify the argument list to select the function unambiguously.")));
+								 func->args_unspecified ?
+								 errhint("Specify the argument list to select the function unambiguously.") : 0));
 						break;
 					case OBJECT_PROCEDURE:
 						ereport(ERROR,
 								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
 								 errmsg("procedure name \"%s\" is not unique",
 										NameListToString(func->objname)),
-								 errhint("Specify the argument list to select the procedure unambiguously.")));
+								 func->args_unspecified ?
+								 errhint("Specify the argument list to select the procedure unambiguously.") : 0));
 						break;
 					case OBJECT_AGGREGATE:
 						ereport(ERROR,
 								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
 								 errmsg("aggregate name \"%s\" is not unique",
 										NameListToString(func->objname)),
-								 errhint("Specify the argument list to select the aggregate unambiguously.")));
+								 func->args_unspecified ?
+								 errhint("Specify the argument list to select the aggregate unambiguously.") : 0));
 						break;
 					case OBJECT_ROUTINE:
 						ereport(ERROR,
 								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
 								 errmsg("routine name \"%s\" is not unique",
 										NameListToString(func->objname)),
-								 errhint("Specify the argument list to select the routine unambiguously.")));
+								 func->args_unspecified ?
+								 errhint("Specify the argument list to select the routine unambiguously.") : 0));
 						break;
 
 					default:
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index c379d72fce..4e46079990 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -150,8 +150,8 @@ LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
 				rightoid;
 
 	Assert(list_length(oper->objargs) == 2);
-	oprleft = linitial(oper->objargs);
-	oprright = lsecond(oper->objargs);
+	oprleft = linitial_node(TypeName, oper->objargs);
+	oprright = lsecond_node(TypeName, oper->objargs);
 
 	if (oprleft == NULL)
 		leftoid = InvalidOid;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index fb3d7ab8ef..3719755a0d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3217,7 +3217,15 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
 		switch (argmode)
 		{
 			case PROARGMODE_IN:
-				modename = "";
+
+				/*
+				 * For procedures, explicitly mark all argument modes, so as
+				 * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
+				 */
+				if (proc->prokind == PROKIND_PROCEDURE)
+					modename = "IN ";
+				else
+					modename = "";
 				isinput = true;
 				break;
 			case PROARGMODE_INOUT:
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 4bc845e57c..244507c97c 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -1898,7 +1898,7 @@ my %tests = (
 		create_sql   => 'CREATE PROCEDURE dump_test.ptest1(a int)
 					   LANGUAGE SQL AS $$ INSERT INTO dump_test.test_table (col1) VALUES (a) $$;',
 		regexp => qr/^
-			\QCREATE PROCEDURE dump_test.ptest1(a integer)\E
+			\QCREATE PROCEDURE dump_test.ptest1(IN a integer)\E
 			\n\s+\QLANGUAGE sql\E
 			\n\s+AS\ \$\$\Q INSERT INTO dump_test.test_table (col1) VALUES (a) \E\$\$;
 			/xm,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 04789614ce..58328c4377 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2024,18 +2024,29 @@ typedef struct GrantStmt
 } GrantStmt;
 
 /*
- * Note: ObjectWithArgs 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.
+ * ObjectWithArgs represents a function/procedure/operator name plus parameter
+ * identification.
+ *
+ * objargs includes only the types of the input parameters of the object.
+ * In some contexts, that will be all we have, and it's enough to look up
+ * objects according to the traditional Postgres rules (i.e., when only input
+ * arguments matter).
+ *
+ * objfuncargs, if not NIL, carries the full specification of the parameter
+ * list, including parameter mode annotations.
+ *
+ * Some grammar productions can set args_unspecified = true instead of
+ * providing parameter info.  In this case, lookup will succeed only if
+ * the object name is unique.  Note that otherwise, NIL parameter lists
+ * mean zero arguments.
  */
 typedef struct ObjectWithArgs
 {
 	NodeTag		type;
 	List	   *objname;		/* qualified name of function/operator */
-	List	   *objargs;		/* list of Typename nodes */
-	bool		args_unspecified;	/* argument list was omitted, so name must
-									 * be unique (note that objargs == NIL
-									 * means zero args) */
+	List	   *objargs;		/* list of Typename nodes (input args only) */
+	List	   *objfuncargs;	/* list of FunctionParameter nodes */
+	bool		args_unspecified;	/* argument list was omitted? */
 } ObjectWithArgs;
 
 /*
@@ -2955,7 +2966,9 @@ typedef enum FunctionParameterMode
 	FUNC_PARAM_OUT = 'o',		/* output only */
 	FUNC_PARAM_INOUT = 'b',		/* both */
 	FUNC_PARAM_VARIADIC = 'v',	/* variadic (always input) */
-	FUNC_PARAM_TABLE = 't'		/* table function output column */
+	FUNC_PARAM_TABLE = 't',		/* table function output column */
+	/* this is not used in pg_proc: */
+	FUNC_PARAM_DEFAULT = 'd'	/* default; effectively same as IN */
 } FunctionParameterMode;
 
 typedef struct FunctionParameter
diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out
index ec884c4d70..46c827f979 100644
--- a/src/test/regress/expected/create_procedure.out
+++ b/src/test/regress/expected/create_procedure.out
@@ -19,17 +19,17 @@ $$;
                         List of functions
  Schema |  Name  | Result data type | Argument data types | Type 
 --------+--------+------------------+---------------------+------
- public | ptest1 |                  | x text              | proc
+ public | ptest1 |                  | IN x text           | proc
 (1 row)
 
 SELECT pg_get_functiondef('ptest1'::regproc);
-                pg_get_functiondef                 
----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest1(x text)+
-  LANGUAGE sql                                    +
- AS $procedure$                                   +
- INSERT INTO cp_test VALUES (1, x);               +
- $procedure$                                      +
+                  pg_get_functiondef                  
+------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest1(IN x text)+
+  LANGUAGE sql                                       +
+ AS $procedure$                                      +
+ INSERT INTO cp_test VALUES (1, x);                  +
+ $procedure$                                         +
  
 (1 row)
 
@@ -46,7 +46,7 @@ SELECT pg_get_functiondef('ptest1'::regproc);
                         List of functions
  Schema |  Name  | Result data type | Argument data types | Type 
 --------+--------+------------------+---------------------+------
- public | ptest1 |                  | x text              | proc
+ public | ptest1 |                  | IN x text           | proc
 (1 row)
 
 SELECT ptest1('x');  -- error
@@ -75,18 +75,18 @@ END;
                         List of functions
  Schema |  Name   | Result data type | Argument data types | Type 
 --------+---------+------------------+---------------------+------
- public | ptest1s |                  | x text              | proc
+ public | ptest1s |                  | IN x text           | proc
 (1 row)
 
 SELECT pg_get_functiondef('ptest1s'::regproc);
-                 pg_get_functiondef                 
-----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest1s(x text)+
-  LANGUAGE sql                                     +
- BEGIN ATOMIC                                      +
-  INSERT INTO cp_test (a, b)                       +
-    VALUES (1, ptest1s.x);                         +
- END                                               +
+                  pg_get_functiondef                   
+-------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest1s(IN x text)+
+  LANGUAGE sql                                        +
+ BEGIN ATOMIC                                         +
+  INSERT INTO cp_test (a, b)                          +
+    VALUES (1, ptest1s.x);                            +
+ END                                                  +
  
 (1 row)
 
@@ -196,16 +196,16 @@ END;
                         List of functions
  Schema |  Name  | Result data type | Argument data types | Type 
 --------+--------+------------------+---------------------+------
- public | ptest8 |                  | x text              | proc
+ public | ptest8 |                  | IN x text           | proc
 (1 row)
 
 SELECT pg_get_functiondef('ptest8'::regproc);
-                pg_get_functiondef                 
----------------------------------------------------
- CREATE OR REPLACE PROCEDURE public.ptest8(x text)+
-  LANGUAGE sql                                    +
- BEGIN ATOMIC                                     +
- END                                              +
+                  pg_get_functiondef                  
+------------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest8(IN x text)+
+  LANGUAGE sql                                       +
+ BEGIN ATOMIC                                        +
+ END                                                 +
  
 (1 row)
 
@@ -278,6 +278,44 @@ CALL ptest11(null, 11, 12, 13);
  23
 (1 row)
 
+-- check resolution of ambiguous DROP commands
+CREATE PROCEDURE ptest10(IN a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT a + b - c $$;
+\df ptest10
+                                   List of functions
+ Schema |  Name   | Result data type |            Argument data types            | Type 
+--------+---------+------------------+-------------------------------------------+------
+ public | ptest10 |                  | IN a integer, IN b integer, IN c integer  | proc
+ public | ptest10 |                  | OUT a integer, IN b integer, IN c integer | proc
+(2 rows)
+
+drop procedure ptest10;  -- fail
+ERROR:  procedure name "ptest10" is not unique
+HINT:  Specify the argument list to select the procedure unambiguously.
+drop procedure ptest10(int, int, int);  -- fail
+ERROR:  procedure name "ptest10" is not unique
+begin;
+drop procedure ptest10(out int, int, int);
+\df ptest10
+                                   List of functions
+ Schema |  Name   | Result data type |           Argument data types            | Type 
+--------+---------+------------------+------------------------------------------+------
+ public | ptest10 |                  | IN a integer, IN b integer, IN c integer | proc
+(1 row)
+
+drop procedure ptest10(int, int, int);  -- now this would work
+rollback;
+begin;
+drop procedure ptest10(in int, int, int);
+\df ptest10
+                                   List of functions
+ Schema |  Name   | Result data type |            Argument data types            | Type 
+--------+---------+------------------+-------------------------------------------+------
+ public | ptest10 |                  | OUT a integer, IN b integer, IN c integer | proc
+(1 row)
+
+drop procedure ptest10(int, int, int);  -- now this would work
+rollback;
 -- various error cases
 CALL version();  -- error: not a procedure
 ERROR:  version() is not a procedure
diff --git a/src/test/regress/sql/create_procedure.sql b/src/test/regress/sql/create_procedure.sql
index b835ab7ec0..75cc0fcf2a 100644
--- a/src/test/regress/sql/create_procedure.sql
+++ b/src/test/regress/sql/create_procedure.sql
@@ -175,6 +175,25 @@ CREATE PROCEDURE ptest11(a OUT int, VARIADIC b int[]) LANGUAGE SQL
 
 CALL ptest11(null, 11, 12, 13);
 
+-- check resolution of ambiguous DROP commands
+
+CREATE PROCEDURE ptest10(IN a int, IN b int, IN c int)
+LANGUAGE SQL AS $$ SELECT a + b - c $$;
+
+\df ptest10
+
+drop procedure ptest10;  -- fail
+drop procedure ptest10(int, int, int);  -- fail
+begin;
+drop procedure ptest10(out int, int, int);
+\df ptest10
+drop procedure ptest10(int, int, int);  -- now this would work
+rollback;
+begin;
+drop procedure ptest10(in int, int, int);
+\df ptest10
+drop procedure ptest10(int, int, int);  -- now this would work
+rollback;
 
 -- various error cases
 
