Odd procedure resolution

Started by Ashutosh Bapatalmost 8 years ago13 messages
#1Ashutosh Bapat
ashutosh.bapat@enterprisedb.com

Hi,
Consider following scenario

create function foo(a int) returns integer as $$begin return a; end;
$$ language plpgsql;
create procedure foo(a float) as $$begin end; $$ language plpgsql;
call foo(1);
psql:proc_func_resolution.sql:8: ERROR: foo(integer) is not a procedure
LINE 1: call foo(1);
^
HINT: To call a function, use SELECT.

to me the error message looks confusing. I am using CALL, which means
I am trying to invoke a "procedure" not a "function" and there exists
one which can be invoked. If I drop function foo() and try call again,
it succeeds.

drop function foo(a int);
DROP FUNCTION
call foo(1);
CALL

Functions and Procedures are two different objects and we enforce
different methods to invoke those, SELECT and CALL resp. So, we should
be able to filter out one or the other and try to find best candidate
of a given kind.

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

#2Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Ashutosh Bapat (#1)
1 attachment(s)
Re: Odd procedure resolution

Incidently the fix looks quite simple. See patch attached.

With this patch we have a diffs in create_procedure test like
  CALL random();  -- error
! ERROR:  random() is not a procedure
  LINE 1: CALL random();
               ^
! HINT:  To call a function, use SELECT.
  CREATE FUNCTION cp_testfunc1(a int) RETURNS int LANGUAGE SQL AS $$
SELECT a $$;
  CREATE TABLE cp_test (a int, b text);
  CREATE PROCEDURE ptest1(x text)
--- 4,13 ----
               ^
  HINT:  No function matches the given name and argument types. You
might need to add explicit type casts.
  CALL random();  -- error
! ERROR:  function random() does not exist
  LINE 1: CALL random();
               ^
! HINT:  No function matches the given name and argument types. You
might need to add explicit type casts.
  CREATE FUNCTION cp_testfunc1(a int) RETURNS int LANGUAGE SQL AS $$
SELECT a $$;
  CREATE TABLE cp_test (a int, b text);
  CREATE PROCEDURE ptest1(x text)

If we replace "function" with "procedure" the new error messages read
"procedure random() does not exist" "No procedure matches the given
...". Those messages look better than "random() is not a procedure".

But I haven't fixed the error messages in this patch. I need to first
see if the changes are acceptable.

On Fri, Mar 23, 2018 at 3:53 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:

Hi,
Consider following scenario

create function foo(a int) returns integer as $$begin return a; end;
$$ language plpgsql;
create procedure foo(a float) as $$begin end; $$ language plpgsql;
call foo(1);
psql:proc_func_resolution.sql:8: ERROR: foo(integer) is not a procedure
LINE 1: call foo(1);
^
HINT: To call a function, use SELECT.

to me the error message looks confusing. I am using CALL, which means
I am trying to invoke a "procedure" not a "function" and there exists
one which can be invoked. If I drop function foo() and try call again,
it succeeds.

drop function foo(a int);
DROP FUNCTION
call foo(1);
CALL

Functions and Procedures are two different objects and we enforce
different methods to invoke those, SELECT and CALL resp. So, we should
be able to filter out one or the other and try to find best candidate
of a given kind.

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

Attachments:

proc_resolution.patchtext/x-patch; charset=US-ASCII; name=proc_resolution.patchDownload
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 52dd400..2a8b78d 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -901,7 +901,7 @@ TypeIsVisible(Oid typid)
  */
 FuncCandidateList
 FuncnameGetCandidates(List *names, int nargs, List *argnames,
-					  bool expand_variadic, bool expand_defaults,
+					  bool expand_variadic, bool expand_defaults, char prokind,
 					  bool missing_ok)
 {
 	FuncCandidateList resultList = NULL;
@@ -948,6 +948,10 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
 		int		   *argnumbers = NULL;
 		FuncCandidateList newResult;
 
+		/* Match prokind if specific one is requested. */
+		if (prokind != PROKIND_INVALID && prokind != procform->prokind)
+			continue;
+
 		if (OidIsValid(namespaceId))
 		{
 			/* Consider only procs in specified namespace */
@@ -1409,7 +1413,8 @@ FunctionIsVisible(Oid funcid)
 		visible = false;
 
 		clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-									  nargs, NIL, false, false, false);
+									  nargs, NIL, false, false,
+									  PROKIND_INVALID, false);
 
 		for (; clist; clist = clist->next)
 		{
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 50d8d81..27530b0 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -803,6 +803,7 @@ lookup_agg_function(List *fnName,
 	 */
 	fdresult = func_get_detail(fnName, NIL, NIL,
 							   nargs, input_types, false, false,
+							   PROKIND_INVALID,
 							   &fnOid, rettype, &retset,
 							   &nvargs, &vatype,
 							   &true_oid_array, NULL);
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index ea5d521..5784691 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -247,6 +247,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 	fdresult = func_get_detail(funcname, fargs, argnames, nargs,
 							   actual_arg_types,
 							   !func_variadic, true,
+							   proc_call ? PROKIND_PROCEDURE : PROKIND_INVALID,
 							   &funcid, &rettype, &retset,
 							   &nvargs, &vatype,
 							   &declared_arg_types, &argdefaults);
@@ -1316,6 +1317,7 @@ func_get_detail(List *funcname,
 				Oid *argtypes,
 				bool expand_variadic,
 				bool expand_defaults,
+				char prokind,
 				Oid *funcid,	/* return value */
 				Oid *rettype,	/* return value */
 				bool *retset,	/* return value */
@@ -1342,7 +1344,7 @@ func_get_detail(List *funcname,
 
 	/* Get list of possible candidates from namespace search */
 	raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
-										   expand_variadic, expand_defaults,
+										   expand_variadic, expand_defaults, prokind,
 										   false);
 
 	/*
@@ -1978,7 +1980,8 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 	/* Passing NULL for argtypes is no longer allowed */
 	Assert(argtypes);
 
-	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError);
+	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false,
+								  PROKIND_INVALID, noError);
 
 	/*
 	 * If no arguments were specified, the name must yield a unique candidate.
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index a007982..6fb93e0 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -94,7 +94,8 @@ regprocin(PG_FUNCTION_ARGS)
 	 * pg_proc entries in the current search path.
 	 */
 	names = stringToQualifiedNameList(pro_name_or_oid);
-	clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
+	clist = FuncnameGetCandidates(names, -1, NIL, false, false,
+								  PROKIND_INVALID, false);
 
 	if (clist == NULL)
 		ereport(ERROR,
@@ -128,7 +129,8 @@ to_regproc(PG_FUNCTION_ARGS)
 	 * entries in the current search path.
 	 */
 	names = stringToQualifiedNameList(pro_name);
-	clist = FuncnameGetCandidates(names, -1, NIL, false, false, true);
+	clist = FuncnameGetCandidates(names, -1, NIL, false, false,
+								  PROKIND_INVALID, true);
 
 	if (clist == NULL || clist->next != NULL)
 		PG_RETURN_NULL();
@@ -176,7 +178,8 @@ regprocout(PG_FUNCTION_ARGS)
 			 * qualify it.
 			 */
 			clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-										  -1, NIL, false, false, false);
+										  -1, NIL, false, false,
+										  PROKIND_INVALID, false);
 			if (clist != NULL && clist->next == NULL &&
 				clist->oid == proid)
 				nspname = NULL;
@@ -263,7 +266,8 @@ regprocedurein(PG_FUNCTION_ARGS)
 	 */
 	parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
 
-	clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false);
+	clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
+								  PROKIND_INVALID, false);
 
 	for (; clist; clist = clist->next)
 	{
@@ -302,7 +306,8 @@ to_regprocedure(PG_FUNCTION_ARGS)
 	 */
 	parseNameAndArgTypes(pro_name, false, &names, &nargs, argtypes);
 
-	clist = FuncnameGetCandidates(names, nargs, NIL, false, false, true);
+	clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
+								  PROKIND_INVALID, true);
 
 	for (; clist; clist = clist->next)
 	{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b0559ca..03bf868 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10755,7 +10755,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
 	if (!force_qualify)
 		p_result = func_get_detail(list_make1(makeString(proname)),
 								   NIL, argnames, nargs, argtypes,
-								   !use_variadic, true,
+								   !use_variadic, true, PROKIND_INVALID,
 								   &p_funcid, &p_rettype,
 								   &p_retset, &p_nvargs, &p_vatype,
 								   &p_true_typeids, NULL);
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 5f8cf49..f12edc8 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -72,6 +72,7 @@ extern FuncCandidateList FuncnameGetCandidates(List *names,
 					  int nargs, List *argnames,
 					  bool expand_variadic,
 					  bool expand_defaults,
+					  char prokind,
 					  bool missing_ok);
 extern bool FunctionIsVisible(Oid funcid);
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bfc9009..8821bb7 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5578,6 +5578,7 @@ DESCR("hash partition CHECK constraint");
 /*
  * Symbolic values for prokind column
  */
+#define PROKIND_INVALID '\0'
 #define PROKIND_FUNCTION 'f'
 #define PROKIND_AGGREGATE 'a'
 #define PROKIND_WINDOW 'w'
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 2e3810f..737852d 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -39,6 +39,7 @@ extern FuncDetailCode func_get_detail(List *funcname,
 				List *fargs, List *fargnames,
 				int nargs, Oid *argtypes,
 				bool expand_variadic, bool expand_defaults,
+				char prokind,
 				Oid *funcid, Oid *rettype,
 				bool *retset, int *nvargs, Oid *vatype,
 				Oid **true_typeids, List **argdefaults);
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Ashutosh Bapat (#2)
Re: Odd procedure resolution

Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> writes:

Incidently the fix looks quite simple. See patch attached.

ISTM this patch effectively proposes to make procedures have their own
namespace yet still live in pg_proc. That is the worst of all possible
worlds IMO. Somewhere early in this patch series, I complained that
procedures should be in a different namespace and therefore not be kept
in pg_proc but in some new catalog. That argument was rejected on the
grounds that SQL requires them to be in the same namespace, which I
wasn't particularly sold on, but that's where we are. If they are in
the same namespace, though, we have to live with the consequences of
that, including ambiguity. Otherwise there will soon be questions
like "well, why can't I create both function foo(int) and procedure
foo(int), seeing that there's no question which of them a particular
statement intends to call?".

regards, tom lane

#4Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Tom Lane (#3)
Re: Odd procedure resolution

On Fri, Mar 23, 2018 at 7:53 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> writes:

Incidently the fix looks quite simple. See patch attached.

ISTM this patch effectively proposes to make procedures have their own
namespace yet still live in pg_proc. That is the worst of all possible
worlds IMO. Somewhere early in this patch series, I complained that
procedures should be in a different namespace and therefore not be kept
in pg_proc but in some new catalog. That argument was rejected on the
grounds that SQL requires them to be in the same namespace, which I
wasn't particularly sold on, but that's where we are. If they are in
the same namespace, though, we have to live with the consequences of
that, including ambiguity. Otherwise there will soon be questions
like "well, why can't I create both function foo(int) and procedure
foo(int), seeing that there's no question which of them a particular
statement intends to call?".

That question did cross my mind and I think that's a valid question.

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company

#5Robert Haas
robertmhaas@gmail.com
In reply to: Ashutosh Bapat (#4)
Re: Odd procedure resolution

On Fri, Mar 23, 2018 at 10:42 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:

On Fri, Mar 23, 2018 at 7:53 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> writes:

Incidently the fix looks quite simple. See patch attached.

ISTM this patch effectively proposes to make procedures have their own
namespace yet still live in pg_proc. That is the worst of all possible
worlds IMO. Somewhere early in this patch series, I complained that
procedures should be in a different namespace and therefore not be kept
in pg_proc but in some new catalog. That argument was rejected on the
grounds that SQL requires them to be in the same namespace, which I
wasn't particularly sold on, but that's where we are. If they are in
the same namespace, though, we have to live with the consequences of
that, including ambiguity. Otherwise there will soon be questions
like "well, why can't I create both function foo(int) and procedure
foo(int), seeing that there's no question which of them a particular
statement intends to call?".

That question did cross my mind and I think that's a valid question.

I agree, but I'm not sure it settles the issue. If you hand somebody
a plate and a slice of pizza and say "eat this", you expect them to
understand that they should try to eat the pizza, not the plate. You
expect this because virtually all human beings over the age of two
understand that pizza is eatable and plates are not. It is similar
reasonable to expect that when the database is asked to call one of
two things, one of which can be called and the other of which cannot,
it might decide to try calling the one that can be called rather than
the one that can't. I think we need procedures and functions to live
in the same namespace because otherwise DROP ROUTINE foo(int) could
find two things equally worthy of being dropped, and we don't want
that to happen (leaving aside the question of whether DROP ROUTINE is
a good idea in the first place). That does not mean -- to me anyway
-- that we've got to make CALL latch onto a function when a procedure
is available.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#5)
Re: Odd procedure resolution

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Mar 23, 2018 at 10:42 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:

On Fri, Mar 23, 2018 at 7:53 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

ISTM this patch effectively proposes to make procedures have their own
namespace yet still live in pg_proc. That is the worst of all possible
worlds IMO.

... That does not mean -- to me anyway
-- that we've got to make CALL latch onto a function when a procedure
is available.

My opinion remains unchanged. If you're unhappy about the system
confusing procedure foo(int) and function foo(real), maybe the answer
is to not overload the name "foo" with such enthusiasm. But putting
kluges into (some of) the lookup rules is just going to lead to its
own problems and surprising results.

In particular, I dislike the idea that this patch would make routine
names appear unique in some cases when they do not in others.
Overloading is complicated/confusing enough without that.

BTW, we seem to have some confusion here already:

regression=# create function foo(int) returns int as 'select $1' language sql;
CREATE FUNCTION
regression=# create procedure foo(text) as 'select $1' language sql;
CREATE PROCEDURE
regression=# drop function foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop routine foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop procedure foo;
ERROR: could not find a procedure named "foo"

The first two errors are what I'd expect, but why is the third
different?

regards, tom lane

#7Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#6)
Re: Odd procedure resolution

On Wed, May 16, 2018 at 3:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

My opinion remains unchanged. If you're unhappy about the system
confusing procedure foo(int) and function foo(real), maybe the answer
is to not overload the name "foo" with such enthusiasm. But putting
kluges into (some of) the lookup rules is just going to lead to its
own problems and surprising results.

In particular, I dislike the idea that this patch would make routine
names appear unique in some cases when they do not in others.
Overloading is complicated/confusing enough without that.

I am not endorsing the patch and haven't looked at it, but I don't buy
the idea that having CALL prefer procedures and SELECT functions would
confuse people more than what we've got already. As we have it,
creating an uncallable object can make CALL fail, which is certainly a
POLA violation. You might be be able to convince me that it's better
than the alternatives, but it can't possibly be *good*.

BTW, we seem to have some confusion here already:

regression=# create function foo(int) returns int as 'select $1' language sql;
CREATE FUNCTION
regression=# create procedure foo(text) as 'select $1' language sql;
CREATE PROCEDURE
regression=# drop function foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop routine foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop procedure foo;
ERROR: could not find a procedure named "foo"

The first two errors are what I'd expect, but why is the third
different?

Good question.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#8Michael Paquier
michael@paquier.xyz
In reply to: Robert Haas (#7)
Re: Odd procedure resolution

On Wed, May 16, 2018 at 03:37:18PM -0400, Robert Haas wrote:

On Wed, May 16, 2018 at 3:29 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

BTW, we seem to have some confusion here already:

regression=# create function foo(int) returns int as 'select $1' language sql;
CREATE FUNCTION
regression=# create procedure foo(text) as 'select $1' language sql;
CREATE PROCEDURE
regression=# drop function foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop routine foo;
ERROR: function name "foo" is not unique
HINT: Specify the argument list to select the function unambiguously.
regression=# drop procedure foo;
ERROR: could not find a procedure named "foo"

The first two errors are what I'd expect, but why is the third
different?

Good question.

I actually find the first error messages ambiguous as well, so that
looks like a bug to me when a lookup is done for those function names.
Shouldn't DROP FUNCTION work only on functions and DROP PROCEDURE only
on procedures? It is documented that DROP ROUTINE can work on
aggregates, functions and procedures, but the docs tell a different
story about DROP PROCEDURE and DROP FUNCTION.
--
Michael

#9Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#6)
1 attachment(s)
Re: Odd procedure resolution

On 5/16/18 15:29, Tom Lane wrote:

My opinion remains unchanged. If you're unhappy about the system
confusing procedure foo(int) and function foo(real), maybe the answer
is to not overload the name "foo" with such enthusiasm. But putting
kluges into (some of) the lookup rules is just going to lead to its
own problems and surprising results.

In particular, I dislike the idea that this patch would make routine
names appear unique in some cases when they do not in others.
Overloading is complicated/confusing enough without that.

I think I have made a mistake here. I was reading in between the lines
of a competitor's documentation that they have functions and procedures
in different name spaces, which made me re-read the SQL standard, which
appears to support that approach.

So I'm proposing here a patch to fix that. It is similar to the patch
proposed earlier in the thread, but more extensive.

One open problem in my patch is that regproc/regprocedure don't have a
way to distinguish functions from procedures. Maybe a two-argument
version of to_regprocedure? This will also affect psql's \ef function
and the like.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v1-0001-Allow-functions-and-procedures-with-the-same-name.patchtext/plain; charset=UTF-8; name=v1-0001-Allow-functions-and-procedures-with-the-same-name.patch; x-mac-creator=0; x-mac-type=0Download
From c87f011a390efe1ee0b5f6f9971e5a5f64791a89 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Thu, 17 May 2018 15:39:53 -0400
Subject: [PATCH v1] Allow functions and procedures with the same name

According to the SQL standard, functions and procedures should have
separate name spaces.  Implement this, by making prokind part of the
primary key of pg_proc.  prokind now only distinguishes functions and
procedures.  Aggregates and window functions are classified under
functions and are distinguished by their old fields proisagg and
proiswindow.  So this partially reverts the changes in
fd1a421fe66173fb9b85d3fe150afde8e812cbe4 that removed those fields.
Various APIs are changed to pass the prokind along with the name and
arguments for a function or procedure lookup.  Conversely, we don't need
to check after the lookup whether we got a function or procedure.

FIXME: regproc and regprocedure currently have no way to pass in
prokind.  They will look for either and fail if the result is not
unique (that is, like a generic "routine" lookup).
---
 doc/src/sgml/catalogs.sgml                    |  24 +-
 src/backend/catalog/aclchk.c                  |   4 +-
 src/backend/catalog/namespace.c               |   8 +-
 src/backend/catalog/objectaddress.c           |   8 +-
 src/backend/catalog/pg_aggregate.c            |   6 +-
 src/backend/catalog/pg_proc.c                 |  60 ++--
 src/backend/catalog/system_views.sql          |   9 +-
 src/backend/commands/alter.c                  |   4 +-
 src/backend/commands/amcmds.c                 |   2 +-
 src/backend/commands/conversioncmds.c         |   3 +-
 src/backend/commands/dropcmds.c               |   2 +-
 src/backend/commands/event_trigger.c          |   2 +-
 src/backend/commands/foreigncmds.c            |   4 +-
 src/backend/commands/functioncmds.c           |  35 +-
 src/backend/commands/operatorcmds.c           |  11 +-
 src/backend/commands/proclang.c               |  18 +-
 src/backend/commands/trigger.c                |   2 +-
 src/backend/commands/tsearchcmds.c            |   4 +-
 src/backend/commands/typecmds.c               |  30 +-
 src/backend/executor/functions.c              |   6 +-
 src/backend/optimizer/util/clauses.c          |   3 +-
 src/backend/parser/analyze.c                  |   3 +-
 src/backend/parser/parse_clause.c             |   3 +-
 src/backend/parser/parse_coerce.c             |   3 +-
 src/backend/parser/parse_expr.c               |  11 +-
 src/backend/parser/parse_func.c               | 214 +++++-------
 src/backend/utils/adt/regproc.c               |  10 +-
 src/backend/utils/adt/ruleutils.c             |   7 +-
 src/backend/utils/cache/lsyscache.c           |  12 +-
 src/backend/utils/cache/syscache.c            |   8 +-
 src/bin/pg_dump/pg_dump.c                     |  44 ++-
 src/bin/pg_dump/t/002_pg_dump.pl              |   4 +
 src/bin/psql/describe.c                       |  30 +-
 src/bin/psql/tab-complete.c                   |  52 +--
 src/include/catalog/indexing.h                |   4 +-
 src/include/catalog/namespace.h               |   1 +
 src/include/catalog/pg_class.dat              |   2 +-
 src/include/catalog/pg_proc.dat               | 306 +++++++++---------
 src/include/catalog/pg_proc.h                 |  11 +-
 src/include/commands/defrem.h                 |   4 +-
 src/include/parser/parse_func.h               |   8 +-
 src/include/utils/lsyscache.h                 |   2 +-
 src/include/utils/syscache.h                  |   2 +-
 src/pl/tcl/pltcl.c                            |   2 +-
 src/test/regress/expected/alter_generic.out   |  22 +-
 .../regress/expected/create_function_3.out    |  10 +-
 .../regress/expected/create_procedure.out     |  27 +-
 src/test/regress/expected/opr_sanity.out      |  40 +--
 src/test/regress/expected/privileges.out      |   2 +-
 src/test/regress/expected/regex.out           |  22 +-
 src/test/regress/expected/rules.out           |  10 +-
 src/test/regress/sql/alter_generic.sql        |   2 +-
 src/test/regress/sql/create_function_3.sql    |   4 +-
 src/test/regress/sql/opr_sanity.sql           |  40 +--
 54 files changed, 584 insertions(+), 583 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3ed9021c2f..dde657b3ce 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -5118,14 +5118,14 @@ <title><structname>pg_proc</structname></title>
 
   <para>
    The catalog <structname>pg_proc</structname> stores information about
-   functions, procedures, aggregate functions, and window functions
+   functions and procedures
    (collectively also known as routines).  See <xref
    linkend="sql-createfunction"/>, <xref linkend="sql-createprocedure"/>, and
    <xref linkend="xfunc"/> for more information.
   </para>
 
   <para>
-   If <structfield>prokind</structfield> indicates that the entry is for an
+   If <structfield>proisagg</structfield> indicates that the entry is for an
    aggregate function, there should be a matching row in
    <structfield>pg_aggregate</structfield>.
   </para>
@@ -5217,9 +5217,23 @@ <title><structname>pg_proc</structname> Columns</title>
       <entry><structfield>prokind</structfield></entry>
       <entry><type>char</type></entry>
       <entry></entry>
-      <entry><literal>f</literal> for a normal function, <literal>p</literal>
-      for a procedure, <literal>a</literal> for an aggregate function, or
-      <literal>w</literal> for a window function</entry>
+      <entry><literal>f</literal> for a function (which includes aggregate
+      functions and window functions), <literal>p</literal> for a
+      procedure</entry>
+     </row>
+
+     <row>
+      <entry><structfield>proisagg</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>Function is an aggregate function</entry>
+     </row>
+
+     <row>
+      <entry><structfield>proiswindow</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>Function is a window function</entry>
      </row>
 
      <row>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 578e4c6592..366c452552 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -835,8 +835,8 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
 						/* includes aggregates and window functions */
 						ScanKeyInit(&key[keycount++],
 									Anum_pg_proc_prokind,
-									BTEqualStrategyNumber, F_CHARNE,
-									CharGetDatum(PROKIND_PROCEDURE));
+									BTEqualStrategyNumber, F_CHAREQ,
+									CharGetDatum(PROKIND_FUNCTION));
 					else if (objtype == OBJECT_PROCEDURE)
 						ScanKeyInit(&key[keycount++],
 									Anum_pg_proc_prokind,
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..f12d06d482 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -919,7 +919,7 @@ TypeIsVisible(Oid typid)
  * candidate is found for other reasons.
  */
 FuncCandidateList
-FuncnameGetCandidates(List *names, int nargs, List *argnames,
+FuncnameGetCandidates(List *names, char prokind, int nargs, List *argnames,
 					  bool expand_variadic, bool expand_defaults,
 					  bool missing_ok)
 {
@@ -952,7 +952,7 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
 	}
 
 	/* Search syscache by name only */
-	catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));
+	catlist = SearchSysCacheList1(PROCNAMEARGSNSPKIND, CStringGetDatum(funcname));
 
 	for (i = 0; i < catlist->n_members; i++)
 	{
@@ -967,6 +967,9 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
 		int		   *argnumbers = NULL;
 		FuncCandidateList newResult;
 
+		if (prokind != PROKIND_ANY && procform->prokind != prokind)
+			continue;
+
 		if (OidIsValid(namespaceId))
 		{
 			/* Consider only procs in specified namespace */
@@ -1428,6 +1431,7 @@ FunctionIsVisible(Oid funcid)
 		visible = false;
 
 		clist = FuncnameGetCandidates(list_make1(makeString(proname)),
+									  procform->prokind,
 									  nargs, NIL, false, false, false);
 
 		for (; clist; clist = clist->next)
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d371c47842..64691776b1 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -224,7 +224,7 @@ static const ObjectPropertyType ObjectProperty[] =
 		ProcedureRelationId,
 		ProcedureOidIndexId,
 		PROCOID,
-		-1,						/* PROCNAMEARGSNSP also takes argument types */
+		-1,						/* PROCNAMEARGSNSPKIND also takes argument types */
 		Anum_pg_proc_proname,
 		Anum_pg_proc_pronamespace,
 		Anum_pg_proc_proowner,
@@ -4047,10 +4047,10 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 		elog(ERROR, "cache lookup failed for procedure %u", procid);
 	procForm = (Form_pg_proc) GETSTRUCT(procTup);
 
-	if (procForm->prokind == PROKIND_AGGREGATE)
-		appendStringInfoString(buffer, "aggregate");
-	else if (procForm->prokind == PROKIND_PROCEDURE)
+	if (procForm->prokind == PROKIND_PROCEDURE)
 		appendStringInfoString(buffer, "procedure");
+	else if (procForm->proisagg)
+		appendStringInfoString(buffer, "aggregate");
 	else						/* function or window function */
 		appendStringInfoString(buffer, "function");
 
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 246776093e..23540ff00c 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -617,7 +617,9 @@ AggregateCreate(const char *aggName,
 							 InvalidOid,	/* no validator */
 							 "aggregate_dummy", /* placeholder proc */
 							 NULL,	/* probin */
-							 PROKIND_AGGREGATE,
+							 PROKIND_FUNCTION,
+							 true,	/* isAgg */
+							 false,	/* isWindowFunc */
 							 false, /* security invoker (currently not
 									 * definable for agg) */
 							 false, /* isLeakProof */
@@ -806,7 +808,7 @@ lookup_agg_function(List *fnName,
 	 * function's return value.  it also returns the true argument types to
 	 * the function.
 	 */
-	fdresult = func_get_detail(fnName, NIL, NIL,
+	fdresult = func_get_detail(fnName, PROKIND_FUNCTION, NIL, NIL,
 							   nargs, input_types, false, false,
 							   &fnOid, rettype, &retset,
 							   &nvargs, &vatype,
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index d783352911..710335f064 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -74,6 +74,8 @@ ProcedureCreate(const char *procedureName,
 				const char *prosrc,
 				const char *probin,
 				char prokind,
+				bool isAgg,
+				bool isWindowFunc,
 				bool security_definer,
 				bool isLeakProof,
 				bool isStrict,
@@ -334,6 +336,8 @@ ProcedureCreate(const char *procedureName,
 	values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
 	values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
 	values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
+	values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
+	values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
 	values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
 	values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
 	values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
@@ -379,10 +383,11 @@ ProcedureCreate(const char *procedureName,
 	tupDesc = RelationGetDescr(rel);
 
 	/* Check for pre-existing definition */
-	oldtup = SearchSysCache3(PROCNAMEARGSNSP,
+	oldtup = SearchSysCache4(PROCNAMEARGSNSPKIND,
 							 PointerGetDatum(procedureName),
 							 PointerGetDatum(parameterTypes),
-							 ObjectIdGetDatum(procNamespace));
+							 ObjectIdGetDatum(procNamespace),
+							 CharGetDatum(prokind));
 
 	if (HeapTupleIsValid(oldtup))
 	{
@@ -400,21 +405,6 @@ ProcedureCreate(const char *procedureName,
 			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
 						   procedureName);
 
-		/* Not okay to change routine kind */
-		if (oldproc->prokind != prokind)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("cannot change routine kind"),
-					 (oldproc->prokind == PROKIND_AGGREGATE ?
-					  errdetail("\"%s\" is an aggregate function.", procedureName) :
-					  oldproc->prokind == PROKIND_FUNCTION ?
-					  errdetail("\"%s\" is a function.", procedureName) :
-					  oldproc->prokind == PROKIND_PROCEDURE ?
-					  errdetail("\"%s\" is a procedure.", procedureName) :
-					  oldproc->prokind == PROKIND_WINDOW ?
-					  errdetail("\"%s\" is a window function.", procedureName) :
-					  0)));
-
 		/*
 		 * Not okay to change the return type of the existing proc, since
 		 * existing rules, views, etc may depend on the return type.
@@ -458,7 +448,7 @@ ProcedureCreate(const char *procedureName,
 		 * names have not been changed, as this could break existing calls. We
 		 * allow adding names to formerly unnamed parameters, though.
 		 */
-		proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+		proargnames = SysCacheGetAttr(PROCNAMEARGSNSPKIND, oldtup,
 									  Anum_pg_proc_proargnames,
 									  &isnull);
 		if (!isnull)
@@ -470,7 +460,7 @@ ProcedureCreate(const char *procedureName,
 			int			n_new_arg_names;
 			int			j;
 
-			proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+			proargmodes = SysCacheGetAttr(PROCNAMEARGSNSPKIND, oldtup,
 										  Anum_pg_proc_proargmodes,
 										  &isnull);
 			if (isnull)
@@ -519,7 +509,7 @@ ProcedureCreate(const char *procedureName,
 						 errhint("Use DROP FUNCTION %s first.",
 								 format_procedure(HeapTupleGetOid(oldtup)))));
 
-			proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+			proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSPKIND, oldtup,
 											 Anum_pg_proc_proargdefaults,
 											 &isnull);
 			Assert(!isnull);
@@ -548,7 +538,35 @@ ProcedureCreate(const char *procedureName,
 			}
 		}
 
-		/*
+		/* Can't change aggregate or window-function status, either */
+		if (oldproc->proisagg != isAgg)
+		{
+			if (oldproc->proisagg)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("function \"%s\" is an aggregate function",
+								procedureName)));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("function \"%s\" is not an aggregate function",
+								procedureName)));
+		}
+		if (oldproc->proiswindow != isWindowFunc)
+		{
+			if (oldproc->proiswindow)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("function \"%s\" is a window function",
+								procedureName)));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("function \"%s\" is not a window function",
+								procedureName)));
+		}
+
+	   /*
 		 * Do not change existing ownership or permissions, either.  Note
 		 * dependency-update code below has to agree with this decision.
 		 */
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 8cd8bf40ac..be88e9fde6 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -332,11 +332,10 @@ CREATE VIEW pg_seclabels AS
 UNION ALL
 SELECT
 	l.objoid, l.classoid, l.objsubid,
-	CASE pro.prokind
-            WHEN 'a' THEN 'aggregate'::text
-            WHEN 'f' THEN 'function'::text
-            WHEN 'p' THEN 'procedure'::text
-            WHEN 'w' THEN 'window'::text END AS objtype,
+	CASE WHEN pro.prokind = 'p' THEN 'procedure'::text
+             WHEN pro.proisagg THEN 'aggregate'::text
+             ELSE 'function'::text
+        END AS objtype,
 	pro.pronamespace AS objnamespace,
 	CASE WHEN pg_function_is_visible(pro.oid)
 	     THEN quote_ident(pro.proname)
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index eff325cc7d..3cc6fe0ced 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -245,7 +245,7 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
 	{
 		Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
 
-		IsThereFunctionInNamespace(new_name, proc->pronargs,
+		IsThereFunctionInNamespace(new_name, proc->prokind, proc->pronargs,
 								   &proc->proargtypes, proc->pronamespace);
 	}
 	else if (classId == CollationRelationId)
@@ -738,7 +738,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
 	{
 		Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
 
-		IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
+		IsThereFunctionInNamespace(NameStr(proc->proname), proc->prokind, proc->pronargs,
 								   &proc->proargtypes, nspOid);
 	}
 	else if (classId == CollationRelationId)
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index f2173450ad..5f3679fad1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -250,7 +250,7 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 				 errmsg("handler function is not specified")));
 
 	/* handlers have one argument of type internal */
-	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+	handlerOid = LookupFuncName(handler_name, PROKIND_FUNCTION, 1, funcargtypes, false);
 
 	/* check that handler has the correct return type */
 	switch (amtype)
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index e36fc23dd8..92e66cec44 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -18,6 +18,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_conversion.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/alter.h"
 #include "commands/conversioncmds.h"
@@ -76,7 +77,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
 	 * Check the existence of the conversion function. Function name could be
 	 * a qualified name.
 	 */
-	funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
+	funcoid = LookupFuncName(func_name, PROKIND_FUNCTION, sizeof(funcargs) / sizeof(Oid),
 							 funcargs, false);
 
 	/* Check it returns VOID, else it's probably the wrong function */
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4b38ef68d9..fc4ce8d22a 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -92,7 +92,7 @@ RemoveObjects(DropStmt *stmt)
 		 */
 		if (stmt->removeType == OBJECT_FUNCTION)
 		{
-			if (get_func_prokind(address.objectId) == PROKIND_AGGREGATE)
+			if (get_func_isagg(address.objectId))
 				ereport(ERROR,
 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 						 errmsg("\"%s\" is an aggregate function",
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e..c8e51f8022 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -237,7 +237,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 						stmt->trigname)));
 
 	/* Find and validate the trigger function. */
-	funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
+	funcoid = LookupFuncName(stmt->funcname, PROKIND_FUNCTION, 0, fargtypes, false);
 	funcrettype = get_func_rettype(funcoid);
 	if (funcrettype != EVTTRIGGEROID)
 		ereport(ERROR,
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 5c53aeeaeb..4d9371fc64 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -478,7 +478,7 @@ lookup_fdw_handler_func(DefElem *handler)
 		return InvalidOid;
 
 	/* handlers have no arguments */
-	handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
+	handlerOid = LookupFuncName((List *) handler->arg, PROKIND_FUNCTION, 0, funcargtypes, false);
 
 	/* check that handler has correct return type */
 	if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
@@ -505,7 +505,7 @@ lookup_fdw_validator_func(DefElem *validator)
 	funcargtypes[0] = TEXTARRAYOID;
 	funcargtypes[1] = OIDOID;
 
-	return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
+	return LookupFuncName((List *) validator->arg, PROKIND_FUNCTION, 2, funcargtypes, false);
 	/* validator's return value is ignored, so we don't check the type */
 }
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 8864d9ae44..48228fec42 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1099,7 +1099,9 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 						   languageValidator,
 						   prosrc_str,	/* converted to text later */
 						   probin_str,	/* converted to text later */
-						   stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW : PROKIND_FUNCTION),
+						   stmt->is_procedure ? PROKIND_PROCEDURE : PROKIND_FUNCTION,
+						   false,	/* not an aggregate */
+						   isWindowFunc,
 						   security,
 						   isLeakProof,
 						   isStrict,
@@ -1127,7 +1129,7 @@ RemoveFunctionById(Oid funcOid)
 {
 	Relation	relation;
 	HeapTuple	tup;
-	char		prokind;
+	bool		isagg;
 
 	/*
 	 * Delete the pg_proc tuple.
@@ -1138,7 +1140,7 @@ RemoveFunctionById(Oid funcOid)
 	if (!HeapTupleIsValid(tup)) /* should not happen */
 		elog(ERROR, "cache lookup failed for function %u", funcOid);
 
-	prokind = ((Form_pg_proc) GETSTRUCT(tup))->prokind;
+	isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
 
 	CatalogTupleDelete(relation, &tup->t_self);
 
@@ -1149,7 +1151,7 @@ RemoveFunctionById(Oid funcOid)
 	/*
 	 * If there's a pg_aggregate tuple, delete that too.
 	 */
-	if (prokind == PROKIND_AGGREGATE)
+	if (isagg)
 	{
 		relation = heap_open(AggregateRelationId, RowExclusiveLock);
 
@@ -1204,7 +1206,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
 					   NameListToString(stmt->func->objname));
 
-	if (procForm->prokind == PROKIND_AGGREGATE)
+	if (procForm->proisagg)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is an aggregate function",
@@ -1526,10 +1528,14 @@ CreateCast(CreateCastStmt *stmt)
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("cast function must not be volatile")));
 #endif
-		if (procstruct->prokind != PROKIND_FUNCTION)
+		if (procstruct->proisagg)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("cast function must be a normal function")));
+					 errmsg("cast function must not be an aggregate function")));
+		if (procstruct->proiswindow)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("cast function must not be a window function")));
 		if (procstruct->proretset)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -1774,10 +1780,14 @@ check_transform_function(Form_pg_proc procstruct)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("transform function must not be volatile")));
-	if (procstruct->prokind != PROKIND_FUNCTION)
+	if (procstruct->proisagg)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("transform function must not be an aggregate function")));
+	if (procstruct->proiswindow)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("transform function must be a normal function")));
+				 errmsg("transform function must not be a window function")));
 	if (procstruct->proretset)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -2056,14 +2066,15 @@ DropTransformById(Oid transformOid)
  * namespace?  If so, raise an appropriate error message.
  */
 void
-IsThereFunctionInNamespace(const char *proname, int pronargs,
+IsThereFunctionInNamespace(const char *proname, char prokind, int pronargs,
 						   oidvector *proargtypes, Oid nspOid)
 {
 	/* check for duplicate name (more friendly than unique-index failure) */
-	if (SearchSysCacheExists3(PROCNAMEARGSNSP,
+	if (SearchSysCacheExists4(PROCNAMEARGSNSPKIND,
 							  CStringGetDatum(proname),
 							  PointerGetDatum(proargtypes),
-							  ObjectIdGetDatum(nspOid)))
+							  ObjectIdGetDatum(nspOid),
+							  CharGetDatum(prokind)))
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_FUNCTION),
 				 errmsg("function %s already exists in schema \"%s\"",
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index f0da4c5279..ec6cb90803 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -40,6 +40,7 @@
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/alter.h"
 #include "commands/defrem.h"
@@ -205,7 +206,7 @@ DefineOperator(List *names, List *parameters)
 		typeId[1] = typeId2;
 		nargs = 2;
 	}
-	functionOid = LookupFuncName(functionName, nargs, typeId, false);
+	functionOid = LookupFuncName(functionName, PROKIND_FUNCTION, nargs, typeId, false);
 
 	/*
 	 * We require EXECUTE rights for the function.  This isn't strictly
@@ -268,7 +269,7 @@ ValidateRestrictionEstimator(List *restrictionName)
 	typeId[2] = INTERNALOID;	/* args list */
 	typeId[3] = INT4OID;		/* varRelid */
 
-	restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+	restrictionOid = LookupFuncName(restrictionName, PROKIND_FUNCTION, 4, typeId, false);
 
 	/* estimators must return float8 */
 	if (get_func_rettype(restrictionOid) != FLOAT8OID)
@@ -309,12 +310,12 @@ ValidateJoinEstimator(List *joinName)
 	 * arguments, but we still allow the old 4-argument form. Try the
 	 * preferred form first.
 	 */
-	joinOid = LookupFuncName(joinName, 5, typeId, true);
+	joinOid = LookupFuncName(joinName, PROKIND_FUNCTION, 5, typeId, true);
 	if (!OidIsValid(joinOid))
-		joinOid = LookupFuncName(joinName, 4, typeId, true);
+		joinOid = LookupFuncName(joinName, PROKIND_FUNCTION, 4, typeId, true);
 	/* If not found, reference the 5-argument signature in error msg */
 	if (!OidIsValid(joinOid))
-		joinOid = LookupFuncName(joinName, 5, typeId, false);
+		joinOid = LookupFuncName(joinName, PROKIND_FUNCTION, 5, typeId, false);
 
 	/* estimators must return float8 */
 	if (get_func_rettype(joinOid) != FLOAT8OID)
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index c900ad9431..32851d5932 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		 * return type.
 		 */
 		funcname = SystemFuncName(pltemplate->tmplhandler);
-		handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+		handlerOid = LookupFuncName(funcname, PROKIND_FUNCTION, 0, funcargtypes, true);
 		if (OidIsValid(handlerOid))
 		{
 			funcrettype = get_func_rettype(handlerOid);
@@ -129,6 +129,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 									  pltemplate->tmplhandler,
 									  pltemplate->tmpllibrary,
 									  PROKIND_FUNCTION,
+									  false,	/* isAgg */
+									  false,	/* isWindowFunc */
 									  false,	/* security_definer */
 									  false,	/* isLeakProof */
 									  false,	/* isStrict */
@@ -154,7 +156,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		{
 			funcname = SystemFuncName(pltemplate->tmplinline);
 			funcargtypes[0] = INTERNALOID;
-			inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+			inlineOid = LookupFuncName(funcname, PROKIND_FUNCTION, 1, funcargtypes, true);
 			if (!OidIsValid(inlineOid))
 			{
 				tmpAddr = ProcedureCreate(pltemplate->tmplinline,
@@ -168,6 +170,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										  pltemplate->tmplinline,
 										  pltemplate->tmpllibrary,
 										  PROKIND_FUNCTION,
+										  false,	/* isAgg */
+										  false,	/* isWindowFunc */
 										  false,	/* security_definer */
 										  false,	/* isLeakProof */
 										  true, /* isStrict */
@@ -196,7 +200,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		{
 			funcname = SystemFuncName(pltemplate->tmplvalidator);
 			funcargtypes[0] = OIDOID;
-			valOid = LookupFuncName(funcname, 1, funcargtypes, true);
+			valOid = LookupFuncName(funcname, PROKIND_FUNCTION, 1, funcargtypes, true);
 			if (!OidIsValid(valOid))
 			{
 				tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
@@ -210,6 +214,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										  pltemplate->tmplvalidator,
 										  pltemplate->tmpllibrary,
 										  PROKIND_FUNCTION,
+										  false,	/* isAgg */
+										  false,	/* isWindowFunc */
 										  false,	/* security_definer */
 										  false,	/* isLeakProof */
 										  true, /* isStrict */
@@ -261,7 +267,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		 * Lookup the PL handler function and check that it is of the expected
 		 * return type
 		 */
-		handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
+		handlerOid = LookupFuncName(stmt->plhandler, PROKIND_FUNCTION, 0, funcargtypes, false);
 		funcrettype = get_func_rettype(handlerOid);
 		if (funcrettype != LANGUAGE_HANDLEROID)
 		{
@@ -290,7 +296,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		if (stmt->plinline)
 		{
 			funcargtypes[0] = INTERNALOID;
-			inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+			inlineOid = LookupFuncName(stmt->plinline, PROKIND_FUNCTION, 1, funcargtypes, false);
 			/* return value is ignored, so we don't check the type */
 		}
 		else
@@ -300,7 +306,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		if (stmt->plvalidator)
 		{
 			funcargtypes[0] = OIDOID;
-			valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+			valOid = LookupFuncName(stmt->plvalidator, PROKIND_FUNCTION, 1, funcargtypes, false);
 			/* return value is ignored, so we don't check the type */
 		}
 		else
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 57519fe8d6..4d84dab02a 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	 * Find and validate the trigger function.
 	 */
 	if (!OidIsValid(funcoid))
-		funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
+		funcoid = LookupFuncName(stmt->funcname, PROKIND_FUNCTION, 0, fargtypes, false);
 	if (!isInternal)
 	{
 		aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 3a843512d1..ede550a143 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
 			nargs = 0;			/* keep compiler quiet */
 	}
 
-	procOid = LookupFuncName(funcName, nargs, typeId, false);
+	procOid = LookupFuncName(funcName, PROKIND_FUNCTION, nargs, typeId, false);
 	if (get_func_rettype(procOid) != retTypeId)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -672,7 +672,7 @@ get_ts_template_func(DefElem *defel, int attnum)
 			nargs = 0;			/* keep compiler quiet */
 	}
 
-	procOid = LookupFuncName(funcName, nargs, typeId, false);
+	procOid = LookupFuncName(funcName, PROKIND_FUNCTION, nargs, typeId, false);
 	if (get_func_rettype(procOid) != retTypeId)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 175ecc8b48..9b953f94e0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1670,6 +1670,8 @@ makeRangeConstructors(const char *name, Oid namespace,
 								 prosrc[i], /* prosrc */
 								 NULL,	/* probin */
 								 PROKIND_FUNCTION,
+								 false,	/* isAgg */
+								 false,	/* isWindowFunc */
 								 false, /* security_definer */
 								 false, /* leakproof */
 								 false, /* isStrict */
@@ -1717,28 +1719,28 @@ findTypeInputFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = CSTRINGOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	argList[1] = OIDOID;
 	argList[2] = INT4OID;
 
-	procOid = LookupFuncName(procname, 3, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 3, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	/* No luck, try it with OPAQUE */
 	argList[0] = OPAQUEOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 
 	if (!OidIsValid(procOid))
 	{
 		argList[1] = OIDOID;
 		argList[2] = INT4OID;
 
-		procOid = LookupFuncName(procname, 3, argList, true);
+		procOid = LookupFuncName(procname, PROKIND_FUNCTION, 3, argList, true);
 	}
 
 	if (OidIsValid(procOid))
@@ -1783,14 +1785,14 @@ findTypeOutputFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	/* No luck, try it with OPAQUE */
 	argList[0] = OPAQUEOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 
 	if (OidIsValid(procOid))
 	{
@@ -1832,14 +1834,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = INTERNALOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	argList[1] = OIDOID;
 	argList[2] = INT4OID;
 
-	procOid = LookupFuncName(procname, 3, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 3, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
@@ -1862,7 +1864,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (OidIsValid(procOid))
 		return procOid;
 
@@ -1885,7 +1887,7 @@ findTypeTypmodinFunction(List *procname)
 	 */
 	argList[0] = CSTRINGARRAYOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1912,7 +1914,7 @@ findTypeTypmodoutFunction(List *procname)
 	 */
 	argList[0] = INT4OID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1939,7 +1941,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = INTERNALOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -2015,7 +2017,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 1, argList, true);
 
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
@@ -2057,7 +2059,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
 	argList[0] = subtype;
 	argList[1] = subtype;
 
-	procOid = LookupFuncName(procname, 2, argList, true);
+	procOid = LookupFuncName(procname, PROKIND_FUNCTION, 2, argList, true);
 
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 23545896d4..584dad6d18 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -241,13 +241,13 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
 		int			n_arg_names;
 		bool		isNull;
 
-		proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+		proargnames = SysCacheGetAttr(PROCNAMEARGSNSPKIND, procedureTuple,
 									  Anum_pg_proc_proargnames,
 									  &isNull);
 		if (isNull)
 			proargnames = PointerGetDatum(NULL);	/* just to be sure */
 
-		proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+		proargmodes = SysCacheGetAttr(PROCNAMEARGSNSPKIND, procedureTuple,
 									  Anum_pg_proc_proargmodes,
 									  &isNull);
 		if (isNull)
@@ -390,7 +390,7 @@ sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
 								  list_make1(param),
 								  pstate->p_last_srf,
 								  NULL,
-								  false,
+								  PROKIND_FUNCTION,
 								  cref->location);
 	}
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 505ae0af85..f5f615fd16 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4482,10 +4482,9 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 
 	/*
 	 * Forget it if the function is not SQL-language or has other showstopper
-	 * properties.  (The prokind and nargs checks are just paranoia.)
+	 * properties.  (The nargs check is just paranoia.)
 	 */
 	if (funcform->prolang != SQLlanguageId ||
-		funcform->prokind != PROKIND_FUNCTION ||
 		funcform->prosecdef ||
 		funcform->proretset ||
 		funcform->prorettype == RECORDOID ||
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 05f57591e4..aa3cde2d3f 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,6 +25,7 @@
 #include "postgres.h"
 
 #include "access/sysattr.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -2599,7 +2600,7 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt)
 							 targs,
 							 pstate->p_last_srf,
 							 stmt->funccall,
-							 true,
+							 PROKIND_PROCEDURE,
 							 stmt->funccall->location);
 
 	stmt->funcexpr = castNode(FuncExpr, node);
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index e1478805c2..99c00bc7ab 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -27,6 +27,7 @@
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "nodes/makefuncs.h"
@@ -972,7 +973,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
 	 */
 	funcargtypes[0] = INTERNALOID;
 
-	handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+	handlerOid = LookupFuncName(rts->method, PROKIND_FUNCTION, 1, funcargtypes, true);
 
 	/* we want error to complain about no-such-method, not no-such-function */
 	if (!OidIsValid(handlerOid))
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index c31a5630b2..0764a67d67 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -834,7 +834,8 @@ build_coercion_expression(Node *node,
 		 */
 		/* Assert(targetTypeId == procstruct->prorettype); */
 		Assert(!procstruct->proretset);
-		Assert(procstruct->prokind == PROKIND_FUNCTION);
+		Assert(!procstruct->proisagg);
+		Assert(!procstruct->proiswindow);
 		nargs = procstruct->pronargs;
 		Assert(nargs >= 1 && nargs <= 3);
 		/* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a9b6..6329db67d6 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -15,6 +15,7 @@
 
 #include "postgres.h"
 
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
@@ -480,7 +481,7 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
 										  list_make1(result),
 										  last_srf,
 										  NULL,
-										  false,
+										  PROKIND_FUNCTION,
 										  location);
 			if (newresult == NULL)
 				unknown_attribute(pstate, result, strVal(n), location);
@@ -630,7 +631,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 											 list_make1(node),
 											 pstate->p_last_srf,
 											 NULL,
-											 false,
+											 PROKIND_FUNCTION,
 											 cref->location);
 				}
 				break;
@@ -678,7 +679,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 											 list_make1(node),
 											 pstate->p_last_srf,
 											 NULL,
-											 false,
+											 PROKIND_FUNCTION,
 											 cref->location);
 				}
 				break;
@@ -739,7 +740,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 											 list_make1(node),
 											 pstate->p_last_srf,
 											 NULL,
-											 false,
+											 PROKIND_FUNCTION,
 											 cref->location);
 				}
 				break;
@@ -1481,7 +1482,7 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
 							 targs,
 							 last_srf,
 							 fn,
-							 false,
+							 PROKIND_FUNCTION,
 							 fn->location);
 }
 
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index ea5d5212b4..1a89085e7c 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -71,7 +71,7 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
-				  Node *last_srf, FuncCall *fn, bool proc_call, int location)
+				  Node *last_srf, FuncCall *fn, char prokind, int location)
 {
 	bool		is_column = (fn == NULL);
 	List	   *agg_order = (fn ? fn->agg_order : NIL);
@@ -244,7 +244,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 
 	setup_parser_errposition_callback(&pcbstate, pstate, location);
 
-	fdresult = func_get_detail(funcname, fargs, argnames, nargs,
+	fdresult = func_get_detail(funcname, prokind, fargs, argnames, nargs,
 							   actual_arg_types,
 							   !func_variadic, true,
 							   &funcid, &rettype, &retset,
@@ -263,7 +263,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 						   actual_arg_types[0], rettype, -1,
 						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
 	}
-	else if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
+	else if (fdresult == FUNCDETAIL_NORMAL)
 	{
 		/*
 		 * Normal function found; was there anything indicating it must be an
@@ -306,26 +306,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 					 errmsg("OVER specified, but %s is not a window function nor an aggregate function",
 							NameListToString(funcname)),
 					 parser_errposition(pstate, location)));
-
-		if (fdresult == FUNCDETAIL_NORMAL && proc_call)
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_FUNCTION),
-					 errmsg("%s is not a procedure",
-							func_signature_string(funcname, nargs,
-												  argnames,
-												  actual_arg_types)),
-					 errhint("To call a function, use SELECT."),
-					 parser_errposition(pstate, location)));
-
-		if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call)
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_FUNCTION),
-					 errmsg("%s is a procedure",
-							func_signature_string(funcname, nargs,
-												  argnames,
-												  actual_arg_types)),
-					 errhint("To call a procedure, use CALL."),
-					 parser_errposition(pstate, location)));
 	}
 	else if (fdresult == FUNCDETAIL_AGGREGATE)
 	{
@@ -336,15 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		Form_pg_aggregate classForm;
 		int			catDirectArgs;
 
-		if (proc_call)
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_FUNCTION),
-					 errmsg("%s is not a procedure",
-							func_signature_string(funcname, nargs,
-												  argnames,
-												  actual_arg_types)),
-					 parser_errposition(pstate, location)));
-
 		tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
 		if (!HeapTupleIsValid(tup)) /* should not happen */
 			elog(ERROR, "cache lookup failed for aggregate %u", funcid);
@@ -546,6 +517,15 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 							 "after all regular arguments of the aggregate."),
 					 parser_errposition(pstate, location)));
 		}
+		else if (prokind == PROKIND_PROCEDURE)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("procedure %s does not exist",
+							func_signature_string(funcname, nargs, argnames,
+												  actual_arg_types)),
+					 errhint("No procedure matches the given name and argument types. "
+							 "You might need to add explicit type casts."),
+					 parser_errposition(pstate, location)));
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -664,7 +644,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		check_srf_call_placement(pstate, last_srf, location);
 
 	/* build the appropriate output structure */
-	if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
+	if (fdresult == FUNCDETAIL_NORMAL)
 	{
 		FuncExpr   *funcexpr = makeNode(FuncExpr);
 
@@ -1310,6 +1290,7 @@ func_select_candidate(int nargs,
  */
 FuncDetailCode
 func_get_detail(List *funcname,
+				char prokind,
 				List *fargs,
 				List *fargnames,
 				int nargs,
@@ -1341,7 +1322,7 @@ func_get_detail(List *funcname,
 		*argdefaults = NIL;
 
 	/* Get list of possible candidates from namespace search */
-	raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
+	raw_candidates = FuncnameGetCandidates(funcname, prokind, nargs, fargnames,
 										   expand_variadic, expand_defaults,
 										   false);
 
@@ -1615,26 +1596,12 @@ func_get_detail(List *funcname,
 			}
 		}
 
-		switch (pform->prokind)
-		{
-			case PROKIND_AGGREGATE:
-				result = FUNCDETAIL_AGGREGATE;
-				break;
-			case PROKIND_FUNCTION:
-				result = FUNCDETAIL_NORMAL;
-				break;
-			case PROKIND_PROCEDURE:
-				result = FUNCDETAIL_PROCEDURE;
-				break;
-			case PROKIND_WINDOW:
-				result = FUNCDETAIL_WINDOWFUNC;
-				break;
-			default:
-				elog(ERROR, "unrecognized prokind: %c", pform->prokind);
-				result = FUNCDETAIL_NORMAL; /* keep compiler quiet */
-				break;
-		}
-
+		if (pform->proisagg)
+			result = FUNCDETAIL_AGGREGATE;
+		else if (pform->proiswindow)
+			result = FUNCDETAIL_WINDOWFUNC;
+		else
+			result = FUNCDETAIL_NORMAL;
 		ReleaseSysCache(ftup);
 		return result;
 	}
@@ -1971,14 +1938,14 @@ func_signature_string(List *funcname, int nargs,
  * else raise an error.
  */
 Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+LookupFuncName(List *funcname, char prokind, int nargs, const Oid *argtypes, bool noError)
 {
 	FuncCandidateList clist;
 
 	/* Passing NULL for argtypes is no longer allowed */
 	Assert(argtypes);
 
-	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError);
+	clist = FuncnameGetCandidates(funcname, prokind, nargs, NIL, false, false, noError);
 
 	/*
 	 * If no arguments were specified, the name must yield a unique candidate.
@@ -1990,11 +1957,20 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 			if (clist->next)
 			{
 				if (!noError)
-					ereport(ERROR,
-							(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
-							 errmsg("function name \"%s\" is not unique",
-									NameListToString(funcname)),
-							 errhint("Specify the argument list to select the function unambiguously.")));
+				{
+					if (prokind == PROKIND_PROCEDURE)
+						ereport(ERROR,
+								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+								 errmsg("procedure name \"%s\" is not unique",
+										NameListToString(funcname)),
+								 errhint("Specify the argument list to select the procedure unambiguously.")));
+					else
+						ereport(ERROR,
+								(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+								 errmsg("function name \"%s\" is not unique",
+										NameListToString(funcname)),
+								 errhint("Specify the argument list to select the function unambiguously.")));
+				}
 			}
 			else
 				return clist->oid;
@@ -2002,10 +1978,18 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 		else
 		{
 			if (!noError)
-				ereport(ERROR,
-						(errcode(ERRCODE_UNDEFINED_FUNCTION),
-						 errmsg("could not find a function named \"%s\"",
-								NameListToString(funcname))));
+			{
+				if (prokind == PROKIND_PROCEDURE)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_FUNCTION),
+							 errmsg("could not find a procedure named \"%s\"",
+									NameListToString(funcname))));
+				else
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_FUNCTION),
+							 errmsg("could not find a function named \"%s\"",
+									NameListToString(funcname))));
+			}
 		}
 	}
 
@@ -2017,11 +2001,20 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 	}
 
 	if (!noError)
-		ereport(ERROR,
-				(errcode(ERRCODE_UNDEFINED_FUNCTION),
-				 errmsg("function %s does not exist",
-						func_signature_string(funcname, nargs,
-											  NIL, argtypes))));
+	{
+		if (prokind == PROKIND_PROCEDURE)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("procedure %s does not exist",
+							func_signature_string(funcname, nargs,
+												  NIL, argtypes))));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("function %s does not exist",
+							func_signature_string(funcname, nargs,
+												  NIL, argtypes))));
+	}
 
 	return InvalidOid;
 }
@@ -2040,16 +2033,29 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 Oid
 LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
 {
+	char		prokind;
 	Oid			argoids[FUNC_MAX_ARGS];
 	int			argcount;
 	int			i;
 	ListCell   *args_item;
 	Oid			oid;
 
-	Assert(objtype == OBJECT_AGGREGATE ||
-		   objtype == OBJECT_FUNCTION ||
-		   objtype == OBJECT_PROCEDURE ||
-		   objtype == OBJECT_ROUTINE);
+	switch (objtype)
+	{
+		case OBJECT_AGGREGATE:
+		case OBJECT_FUNCTION:
+			prokind = PROKIND_FUNCTION;
+			break;
+		case OBJECT_PROCEDURE:
+			prokind = PROKIND_PROCEDURE;
+			break;
+		case OBJECT_ROUTINE:
+			prokind = PROKIND_ANY;
+			break;
+		default:
+			elog(ERROR, "unexpected objtype: %d", (int) objtype);
+			prokind = 0;
+	}
 
 	argcount = list_length(func->objargs);
 	if (argcount > FUNC_MAX_ARGS)
@@ -2069,60 +2075,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
 		args_item = lnext(args_item);
 	}
 
+	oid = LookupFuncName(func->objname, prokind, func->args_unspecified ? -1 : argcount, argoids,
+						 objtype != OBJECT_AGGREGATE ? noError : true);
+
 	/*
-	 * When looking for a function or routine, we pass noError through to
-	 * LookupFuncName and let it make any error messages.  Otherwise, we make
-	 * our own errors for the aggregate and procedure cases.
+	 * Handle error checking for OBJECT_AGGREGATE here, since LookupFuncName()
+	 * doesn't distinguish aggregates.
 	 */
-	oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
-						 (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
-
-	if (objtype == OBJECT_FUNCTION)
-	{
-		/* Make sure it's a function, not a procedure */
-		if (oid && get_func_prokind(oid) == PROKIND_PROCEDURE)
-		{
-			if (noError)
-				return InvalidOid;
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("%s is not a function",
-							func_signature_string(func->objname, argcount,
-												  NIL, argoids))));
-		}
-	}
-	else if (objtype == OBJECT_PROCEDURE)
-	{
-		if (!OidIsValid(oid))
-		{
-			if (noError)
-				return InvalidOid;
-			else if (func->args_unspecified)
-				ereport(ERROR,
-						(errcode(ERRCODE_UNDEFINED_FUNCTION),
-						 errmsg("could not find a procedure named \"%s\"",
-								NameListToString(func->objname))));
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_UNDEFINED_FUNCTION),
-						 errmsg("procedure %s does not exist",
-								func_signature_string(func->objname, argcount,
-													  NIL, argoids))));
-		}
-
-		/* Make sure it's a procedure */
-		if (get_func_prokind(oid) != PROKIND_PROCEDURE)
-		{
-			if (noError)
-				return InvalidOid;
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("%s is not a procedure",
-							func_signature_string(func->objname, argcount,
-												  NIL, argoids))));
-		}
-	}
-	else if (objtype == OBJECT_AGGREGATE)
+	if (objtype == OBJECT_AGGREGATE)
 	{
 		if (!OidIsValid(oid))
 		{
@@ -2147,7 +2107,7 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
 		}
 
 		/* Make sure it's an aggregate */
-		if (get_func_prokind(oid) != PROKIND_AGGREGATE)
+		if (!get_func_isagg(oid))
 		{
 			if (noError)
 				return InvalidOid;
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index a0079821fe..3c321b1a12 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -94,7 +94,7 @@ regprocin(PG_FUNCTION_ARGS)
 	 * pg_proc entries in the current search path.
 	 */
 	names = stringToQualifiedNameList(pro_name_or_oid);
-	clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
+	clist = FuncnameGetCandidates(names, PROKIND_ANY, -1, NIL, false, false, false);
 
 	if (clist == NULL)
 		ereport(ERROR,
@@ -128,7 +128,7 @@ to_regproc(PG_FUNCTION_ARGS)
 	 * entries in the current search path.
 	 */
 	names = stringToQualifiedNameList(pro_name);
-	clist = FuncnameGetCandidates(names, -1, NIL, false, false, true);
+	clist = FuncnameGetCandidates(names, PROKIND_ANY, -1, NIL, false, false, true);
 
 	if (clist == NULL || clist->next != NULL)
 		PG_RETURN_NULL();
@@ -176,7 +176,7 @@ regprocout(PG_FUNCTION_ARGS)
 			 * qualify it.
 			 */
 			clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-										  -1, NIL, false, false, false);
+										  PROKIND_ANY, -1, NIL, false, false, false);
 			if (clist != NULL && clist->next == NULL &&
 				clist->oid == proid)
 				nspname = NULL;
@@ -263,7 +263,7 @@ regprocedurein(PG_FUNCTION_ARGS)
 	 */
 	parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
 
-	clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false);
+	clist = FuncnameGetCandidates(names, PROKIND_ANY, nargs, NIL, false, false, false);
 
 	for (; clist; clist = clist->next)
 	{
@@ -302,7 +302,7 @@ to_regprocedure(PG_FUNCTION_ARGS)
 	 */
 	parseNameAndArgTypes(pro_name, false, &names, &nargs, argtypes);
 
-	clist = FuncnameGetCandidates(names, nargs, NIL, false, false, true);
+	clist = FuncnameGetCandidates(names, PROKIND_ANY, nargs, NIL, false, false, true);
 
 	for (; clist; clist = clist->next)
 	{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 065238b0fe..e8cdb6d883 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2514,7 +2514,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 	proc = (Form_pg_proc) GETSTRUCT(proctup);
 	name = NameStr(proc->proname);
 
-	if (proc->prokind == PROKIND_AGGREGATE)
+	if (proc->proisagg)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("\"%s\" is an aggregate function", name)));
@@ -2546,7 +2546,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 	/* Emit some miscellaneous options on one line */
 	oldlen = buf.len;
 
-	if (proc->prokind == PROKIND_WINDOW)
+	if (proc->proiswindow)
 		appendStringInfoString(&buf, " WINDOW");
 	switch (proc->provolatile)
 	{
@@ -2854,7 +2854,7 @@ print_function_arguments(StringInfo buf, HeapTuple proctup,
 	}
 
 	/* Check for special treatment of ordered-set aggregates */
-	if (proc->prokind == PROKIND_AGGREGATE)
+	if (proc->proisagg)
 	{
 		HeapTuple	aggtup;
 		Form_pg_aggregate agg;
@@ -10785,6 +10785,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
 	 */
 	if (!force_qualify)
 		p_result = func_get_detail(list_make1(makeString(proname)),
+								   procform->prokind,
 								   NIL, argnames, nargs, argtypes,
 								   !use_variadic, true,
 								   &p_funcid, &p_rettype,
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index bba595ad1d..51b6b4f7bb 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1600,20 +1600,20 @@ func_parallel(Oid funcid)
 }
 
 /*
- * get_func_prokind
- *	   Given procedure id, return the routine kind.
+ * get_func_isagg
+ *	   Given procedure id, return the function's proisagg field.
  */
-char
-get_func_prokind(Oid funcid)
+bool
+get_func_isagg(Oid funcid)
 {
 	HeapTuple	tp;
-	char		result;
+	bool		result;
 
 	tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 	if (!HeapTupleIsValid(tp))
 		elog(ERROR, "cache lookup failed for function %u", funcid);
 
-	result = ((Form_pg_proc) GETSTRUCT(tp))->prokind;
+	result = ((Form_pg_proc) GETSTRUCT(tp))->proisagg;
 	ReleaseSysCache(tp);
 	return result;
 }
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b381782a3..aff1fc9171 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -584,14 +584,14 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		32
 	},
-	{ProcedureRelationId,		/* PROCNAMEARGSNSP */
-		ProcedureNameArgsNspIndexId,
-		3,
+	{ProcedureRelationId,		/* PROCNAMEARGSNSPKIND */
+		ProcedureNameArgsNspKindIndexId,
+		4,
 		{
 			Anum_pg_proc_proname,
 			Anum_pg_proc_proargtypes,
 			Anum_pg_proc_pronamespace,
-			0
+			Anum_pg_proc_prokind,
 		},
 		128
 	},
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d6ceb72c05..e58d83e754 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5424,15 +5424,11 @@ getAggregates(Archive *fout, int *numAggs)
 		PQExpBuffer racl_subquery = createPQExpBuffer();
 		PQExpBuffer initacl_subquery = createPQExpBuffer();
 		PQExpBuffer initracl_subquery = createPQExpBuffer();
-		const char *agg_check;
 
 		buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
 						initracl_subquery, "p.proacl", "p.proowner", "'f'",
 						dopt->binary_upgrade);
 
-		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
-					 : "p.proisagg");
-
 		appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
 						  "p.proname AS aggname, "
 						  "p.pronamespace AS aggnamespace, "
@@ -5447,7 +5443,7 @@ getAggregates(Archive *fout, int *numAggs)
 						  "(p.oid = pip.objoid "
 						  "AND pip.classoid = 'pg_proc'::regclass "
 						  "AND pip.objsubid = 0) "
-						  "WHERE %s AND ("
+						  "WHERE p.proisagg AND ("
 						  "p.pronamespace != "
 						  "(SELECT oid FROM pg_namespace "
 						  "WHERE nspname = 'pg_catalog') OR "
@@ -5456,8 +5452,7 @@ getAggregates(Archive *fout, int *numAggs)
 						  acl_subquery->data,
 						  racl_subquery->data,
 						  initacl_subquery->data,
-						  initracl_subquery->data,
-						  agg_check);
+						  initracl_subquery->data);
 		if (dopt->binary_upgrade)
 			appendPQExpBufferStr(query,
 								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
@@ -5638,15 +5633,11 @@ getFuncs(Archive *fout, int *numFuncs)
 		PQExpBuffer racl_subquery = createPQExpBuffer();
 		PQExpBuffer initacl_subquery = createPQExpBuffer();
 		PQExpBuffer initracl_subquery = createPQExpBuffer();
-		const char *not_agg_check;
 
 		buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
 						initracl_subquery, "p.proacl", "p.proowner", "'f'",
 						dopt->binary_upgrade);
 
-		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
-						 : "NOT p.proisagg");
-
 		appendPQExpBuffer(query,
 						  "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
 						  "p.pronargs, p.proargtypes, p.prorettype, "
@@ -5661,7 +5652,7 @@ getFuncs(Archive *fout, int *numFuncs)
 						  "(p.oid = pip.objoid "
 						  "AND pip.classoid = 'pg_proc'::regclass "
 						  "AND pip.objsubid = 0) "
-						  "WHERE %s"
+						  "WHERE NOT p.proisagg"
 						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
 						  "WHERE classid = 'pg_proc'::regclass AND "
 						  "objid = p.oid AND deptype = 'i')"
@@ -5681,7 +5672,6 @@ getFuncs(Archive *fout, int *numFuncs)
 						  initacl_subquery->data,
 						  initracl_subquery->data,
 						  username_subquery,
-						  not_agg_check,
 						  g_last_builtin_oid,
 						  g_last_builtin_oid);
 		if (dopt->binary_upgrade)
@@ -11474,6 +11464,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	char	   *proargnames;
 	char	   *protrftypes;
 	char	   *prokind;
+	char	   *proiswindow;
 	char	   *provolatile;
 	char	   *proisstrict;
 	char	   *prosecdef;
@@ -11514,7 +11505,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
 						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
 						  "array_to_string(protrftypes, ' ') AS protrftypes, "
-						  "prokind, provolatile, proisstrict, prosecdef, "
+						  "prokind, proiswindow, provolatile, proisstrict, prosecdef, "
 						  "proleakproof, proconfig, procost, prorows, "
 						  "proparallel, "
 						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
@@ -11533,8 +11524,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
 						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
 						  "array_to_string(protrftypes, ' ') AS protrftypes, "
-						  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
-						  "provolatile, proisstrict, prosecdef, "
+						  "'f' AS prokind, "
+						  "proiswindow, provolatile, proisstrict, prosecdef, "
 						  "proleakproof, proconfig, procost, prorows, "
 						  "proparallel, "
 						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
@@ -11553,8 +11544,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
 						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
 						  "array_to_string(protrftypes, ' ') AS protrftypes, "
-						  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
-						  "provolatile, proisstrict, prosecdef, "
+						  "'f' AS prokind, "
+						  "proiswindow, provolatile, proisstrict, prosecdef, "
 						  "proleakproof, proconfig, procost, prorows, "
 						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
 						  "FROM pg_catalog.pg_proc "
@@ -11571,8 +11562,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
 						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
 						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
-						  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
-						  "provolatile, proisstrict, prosecdef, "
+						  "'f' AS prokind, "
+						  "proiswindow, provolatile, proisstrict, prosecdef, "
 						  "proleakproof, proconfig, procost, prorows, "
 						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
 						  "FROM pg_catalog.pg_proc "
@@ -11590,8 +11581,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
 						  "pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
 						  "pg_catalog.pg_get_function_result(oid) AS funcresult, "
-						  "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind, "
-						  "provolatile, proisstrict, prosecdef, "
+						  "'f' AS prokind, "
+						  "proiswindow, provolatile, proisstrict, prosecdef, "
 						  "false AS proleakproof, "
 						  " proconfig, procost, prorows, "
 						  "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
@@ -11604,7 +11595,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 		appendPQExpBuffer(query,
 						  "SELECT proretset, prosrc, probin, "
 						  "proallargtypes, proargmodes, proargnames, "
-						  "'f' AS prokind, "
+						  "'f' AS prokind, false AS proiswindow, "
 						  "provolatile, proisstrict, prosecdef, "
 						  "false AS proleakproof, "
 						  "proconfig, procost, prorows, "
@@ -11618,7 +11609,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 		appendPQExpBuffer(query,
 						  "SELECT proretset, prosrc, probin, "
 						  "proallargtypes, proargmodes, proargnames, "
-						  "'f' AS prokind, "
+						  "'f' AS prokind, false AS proiswindow, "
 						  "provolatile, proisstrict, prosecdef, "
 						  "false AS proleakproof, "
 						  "null AS proconfig, 0 AS procost, 0 AS prorows, "
@@ -11634,7 +11625,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 						  "null AS proallargtypes, "
 						  "null AS proargmodes, "
 						  "proargnames, "
-						  "'f' AS prokind, "
+						  "'f' AS prokind, false AS proiswindow, "
 						  "provolatile, proisstrict, prosecdef, "
 						  "false AS proleakproof, "
 						  "null AS proconfig, 0 AS procost, 0 AS prorows, "
@@ -11668,6 +11659,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 	else
 		protrftypes = NULL;
 	prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
+	proiswindow = PQgetvalue(res, 0, PQfnumber(res, "proiswindow"));
 	provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
 	proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
 	prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
@@ -11841,7 +11833,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
 		}
 	}
 
-	if (prokind[0] == PROKIND_WINDOW)
+	if (proiswindow[0] == 't')
 		appendPQExpBufferStr(q, " WINDOW");
 
 	if (provolatile[0] != PROVOLATILE_VOLATILE)
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index fe036b57ee..dc0883ca27 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2995,6 +2995,8 @@
 						   provariadic,
 						   protransform,
 						   prokind,
+						   proisagg,
+						   proiswindow,
 						   prosecdef,
 						   proleakproof,
 						   proisstrict,
@@ -3027,6 +3029,8 @@
 		\QGRANT SELECT(provariadic) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(protransform) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(prokind) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
+		\QGRANT SELECT(proisagg) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
+		\QGRANT SELECT(proiswindow) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(prosecdef) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(proleakproof) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
 		\QGRANT SELECT(proisstrict) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index e5b3c1ebf9..a71f2a67d6 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -100,20 +100,12 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
 						  "  pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
 						  gettext_noop("Argument data types"));
 
-	if (pset.sversion >= 110000)
-		appendPQExpBuffer(&buf,
-						  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
-						  "FROM pg_catalog.pg_proc p\n"
-						  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-						  "WHERE p.prokind = 'a'\n",
-						  gettext_noop("Description"));
-	else
-		appendPQExpBuffer(&buf,
-						  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
-						  "FROM pg_catalog.pg_proc p\n"
-						  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-						  "WHERE p.proisagg\n",
-						  gettext_noop("Description"));
+	appendPQExpBuffer(&buf,
+					  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
+					  "FROM pg_catalog.pg_proc p\n"
+					  "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
+					  "WHERE p.proisagg\n",
+					  gettext_noop("Description"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -358,18 +350,18 @@ describeFunctions(const char *functypes, const char *pattern, bool verbose, bool
 		appendPQExpBuffer(&buf,
 						  "  pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
 						  "  pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
-						  " CASE p.prokind\n"
-						  "  WHEN 'a' THEN '%s'\n"
-						  "  WHEN 'w' THEN '%s'\n"
-						  "  WHEN 'p' THEN '%s'\n"
+						  " CASE\n"
+						  "  WHEN p.prokind = 'p' THEN '%s'\n"
+						  "  WHEN p.proisagg THEN '%s'\n"
+						  "  WHEN p.proiswindow THEN '%s'\n"
 						  "  ELSE '%s'\n"
 						  " END as \"%s\"",
 						  gettext_noop("Result data type"),
 						  gettext_noop("Argument data types"),
+						  gettext_noop("proc"),
 		/* translator: "agg" is short for "aggregate" */
 						  gettext_noop("agg"),
 						  gettext_noop("window"),
-						  gettext_noop("proc"),
 						  gettext_noop("func"),
 						  gettext_noop("Type"));
 	else if (pset.sversion >= 80400)
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7bb47eadc6..41b26accb3 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -389,39 +389,21 @@ do { \
  * Assembly instructions for schema queries
  */
 
-static const SchemaQuery Query_for_list_of_aggregates[] = {
-	{
-		/* min_server_version */
-		110000,
-		/* catname */
-		"pg_catalog.pg_proc p",
-		/* selcondition */
-		"p.prokind = 'a'",
-		/* viscondition */
-		"pg_catalog.pg_function_is_visible(p.oid)",
-		/* namespace */
-		"p.pronamespace",
-		/* result */
-		"pg_catalog.quote_ident(p.proname)",
-		/* qualresult */
-		NULL
-	},
-	{
-		/* min_server_version */
-		0,
-		/* catname */
-		"pg_catalog.pg_proc p",
-		/* selcondition */
-		"p.proisagg",
-		/* viscondition */
-		"pg_catalog.pg_function_is_visible(p.oid)",
-		/* namespace */
-		"p.pronamespace",
-		/* result */
-		"pg_catalog.quote_ident(p.proname)",
-		/* qualresult */
-		NULL
-	}
+static const SchemaQuery Query_for_list_of_aggregates = {
+	/* min_server_version */
+	0,
+	/* catname */
+	"pg_catalog.pg_proc p",
+	/* selcondition */
+	"p.proisagg",
+	/* viscondition */
+	"pg_catalog.pg_function_is_visible(p.oid)",
+	/* namespace */
+	"p.pronamespace",
+	/* result */
+	"pg_catalog.quote_ident(p.proname)",
+	/* qualresult */
+	NULL
 };
 
 static const SchemaQuery Query_for_list_of_datatypes = {
@@ -1191,7 +1173,7 @@ typedef struct
 
 static const pgsql_thing_t words_after_create[] = {
 	{"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER},
-	{"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates},
+	{"AGGREGATE", NULL, NULL, &Query_for_list_of_aggregates},
 	{"CAST", NULL, NULL, NULL}, /* Casts have complex structures for names, so
 								 * skip it */
 	{"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"},
@@ -3634,7 +3616,7 @@ psql_completion(const char *text, int start, int end)
 			COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 	}
 	else if (TailMatchesCS1("\\da*"))
-		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
 	else if (TailMatchesCS1("\\dA*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
 	else if (TailMatchesCS1("\\db*"))
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 24915824ca..4a82055982 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -215,8 +215,8 @@ DECLARE_UNIQUE_INDEX(pg_pltemplate_name_index, 1137, on pg_pltemplate using btre
 
 DECLARE_UNIQUE_INDEX(pg_proc_oid_index, 2690, on pg_proc using btree(oid oid_ops));
 #define ProcedureOidIndexId  2690
-DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index, 2691, on pg_proc using btree(proname name_ops, proargtypes oidvector_ops, pronamespace oid_ops));
-#define ProcedureNameArgsNspIndexId  2691
+DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_kind_index, 2691, on pg_proc using btree(proname name_ops, proargtypes oidvector_ops, pronamespace oid_ops, prokind char_ops));
+#define ProcedureNameArgsNspKindIndexId  2691
 
 DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid oid_ops));
 #define RewriteOidIndexId  2692
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 7991de5e21..45c68c37cb 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -80,6 +80,7 @@ extern Oid	TypenameGetTypid(const char *typname);
 extern bool TypeIsVisible(Oid typid);
 
 extern FuncCandidateList FuncnameGetCandidates(List *names,
+					  char prokind,
 					  int nargs, List *argnames,
 					  bool expand_variadic,
 					  bool expand_defaults,
diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat
index 9fffdef379..045f255359 100644
--- a/src/include/catalog/pg_class.dat
+++ b/src/include/catalog/pg_class.dat
@@ -47,7 +47,7 @@
   reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
   reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
   reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
-  relpersistence => 'p', relkind => 'r', relnatts => '28', relchecks => '0',
+  relpersistence => 'p', relkind => 'r', relnatts => '30', relchecks => '0',
   relhasoids => 't', relhasrules => 'f', relhastriggers => 'f',
   relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f',
   relispopulated => 't', relreplident => 'n', relispartition => 'f',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 66c6c224a8..0f905ceca5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1618,7 +1618,7 @@
   proname => 'array_agg_finalfn', proisstrict => 'f', prorettype => 'anyarray',
   proargtypes => 'internal anynonarray', prosrc => 'array_agg_finalfn' },
 { oid => '2335', descr => 'concatenate aggregate input into an array',
-  proname => 'array_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'array_agg', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anynonarray',
   prosrc => 'aggregate_dummy' },
 { oid => '4051', descr => 'aggregate transition function',
@@ -1630,7 +1630,7 @@
   prorettype => 'anyarray', proargtypes => 'internal anyarray',
   prosrc => 'array_agg_array_finalfn' },
 { oid => '4053', descr => 'concatenate aggregate input into an array',
-  proname => 'array_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'array_agg', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
   prosrc => 'aggregate_dummy' },
 { oid => '3218',
@@ -4776,7 +4776,7 @@
   proname => 'string_agg_finalfn', proisstrict => 'f', prorettype => 'text',
   proargtypes => 'internal', prosrc => 'string_agg_finalfn' },
 { oid => '3538', descr => 'concatenate aggregate input into a string',
-  proname => 'string_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'string_agg', proisagg => 't', proisstrict => 'f',
   prorettype => 'text', proargtypes => 'text text',
   prosrc => 'aggregate_dummy' },
 { oid => '3543', descr => 'aggregate transition function',
@@ -4788,7 +4788,7 @@
   prorettype => 'bytea', proargtypes => 'internal',
   prosrc => 'bytea_string_agg_finalfn' },
 { oid => '3545', descr => 'concatenate aggregate input into a bytea',
-  proname => 'string_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'string_agg', proisagg => 't', proisstrict => 'f',
   prorettype => 'bytea', proargtypes => 'bytea bytea',
   prosrc => 'aggregate_dummy' },
 
@@ -6145,408 +6145,408 @@
 
 { oid => '2100',
   descr => 'the average (arithmetic mean) as numeric of all bigint values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2101',
   descr => 'the average (arithmetic mean) as numeric of all integer values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2102',
   descr => 'the average (arithmetic mean) as numeric of all smallint values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2103',
   descr => 'the average (arithmetic mean) as numeric of all numeric values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2104',
   descr => 'the average (arithmetic mean) as float8 of all float4 values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2105',
   descr => 'the average (arithmetic mean) as float8 of all float8 values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'avg', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2106',
   descr => 'the average (arithmetic mean) as interval of all interval values',
-  proname => 'avg', prokind => 'a', proisstrict => 'f',
+  proname => 'avg', proisagg => 't', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2107', descr => 'sum as numeric across all bigint input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2108', descr => 'sum as bigint across all integer input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2109', descr => 'sum as bigint across all smallint input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2110', descr => 'sum as float4 across all float4 input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'float4',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'float4',
   proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2111', descr => 'sum as float8 across all float8 input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2112', descr => 'sum as money across all money input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'money',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'money',
   proargtypes => 'money', prosrc => 'aggregate_dummy' },
 { oid => '2113', descr => 'sum as interval across all interval input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f',
+  proname => 'sum', proisagg => 't', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
 { oid => '2114', descr => 'sum as numeric across all numeric input values',
-  proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'sum', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 
 { oid => '2115', descr => 'maximum value of all bigint input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2116', descr => 'maximum value of all integer input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'int4',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'int4',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2117', descr => 'maximum value of all smallint input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'int2',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'int2',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2118', descr => 'maximum value of all oid input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'oid',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'oid',
   proargtypes => 'oid', prosrc => 'aggregate_dummy' },
 { oid => '2119', descr => 'maximum value of all float4 input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'float4',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'float4',
   proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2120', descr => 'maximum value of all float8 input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2121', descr => 'maximum value of all abstime input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'abstime',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'abstime',
   proargtypes => 'abstime', prosrc => 'aggregate_dummy' },
 { oid => '2122', descr => 'maximum value of all date input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'date',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'date',
   proargtypes => 'date', prosrc => 'aggregate_dummy' },
 { oid => '2123', descr => 'maximum value of all time input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'time',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'time',
   proargtypes => 'time', prosrc => 'aggregate_dummy' },
 { oid => '2124',
   descr => 'maximum value of all time with time zone input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'timetz',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'timetz',
   proargtypes => 'timetz', prosrc => 'aggregate_dummy' },
 { oid => '2125', descr => 'maximum value of all money input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'money',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'money',
   proargtypes => 'money', prosrc => 'aggregate_dummy' },
 { oid => '2126', descr => 'maximum value of all timestamp input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f',
+  proname => 'max', proisagg => 't', proisstrict => 'f',
   prorettype => 'timestamp', proargtypes => 'timestamp',
   prosrc => 'aggregate_dummy' },
 { oid => '2127',
   descr => 'maximum value of all timestamp with time zone input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f',
+  proname => 'max', proisagg => 't', proisstrict => 'f',
   prorettype => 'timestamptz', proargtypes => 'timestamptz',
   prosrc => 'aggregate_dummy' },
 { oid => '2128', descr => 'maximum value of all interval input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f',
+  proname => 'max', proisagg => 't', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
 { oid => '2129', descr => 'maximum value of all text input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'text',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'text',
   proargtypes => 'text', prosrc => 'aggregate_dummy' },
 { oid => '2130', descr => 'maximum value of all numeric input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2050', descr => 'maximum value of all anyarray input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f',
+  proname => 'max', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
   prosrc => 'aggregate_dummy' },
 { oid => '2244', descr => 'maximum value of all bpchar input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'bpchar',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'bpchar',
   proargtypes => 'bpchar', prosrc => 'aggregate_dummy' },
 { oid => '2797', descr => 'maximum value of all tid input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'tid',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'tid',
   proargtypes => 'tid', prosrc => 'aggregate_dummy' },
 { oid => '3564', descr => 'maximum value of all inet input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'inet',
   proargtypes => 'inet', prosrc => 'aggregate_dummy' },
 
 { oid => '2131', descr => 'minimum value of all bigint input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2132', descr => 'minimum value of all integer input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int4',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'int4',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2133', descr => 'minimum value of all smallint input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int2',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'int2',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2134', descr => 'minimum value of all oid input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'oid',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'oid',
   proargtypes => 'oid', prosrc => 'aggregate_dummy' },
 { oid => '2135', descr => 'minimum value of all float4 input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'float4',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'float4',
   proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2136', descr => 'minimum value of all float8 input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2137', descr => 'minimum value of all abstime input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'abstime',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'abstime',
   proargtypes => 'abstime', prosrc => 'aggregate_dummy' },
 { oid => '2138', descr => 'minimum value of all date input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'date',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'date',
   proargtypes => 'date', prosrc => 'aggregate_dummy' },
 { oid => '2139', descr => 'minimum value of all time input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'time',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'time',
   proargtypes => 'time', prosrc => 'aggregate_dummy' },
 { oid => '2140',
   descr => 'minimum value of all time with time zone input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'timetz',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'timetz',
   proargtypes => 'timetz', prosrc => 'aggregate_dummy' },
 { oid => '2141', descr => 'minimum value of all money input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'money',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'money',
   proargtypes => 'money', prosrc => 'aggregate_dummy' },
 { oid => '2142', descr => 'minimum value of all timestamp input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f',
+  proname => 'min', proisagg => 't', proisstrict => 'f',
   prorettype => 'timestamp', proargtypes => 'timestamp',
   prosrc => 'aggregate_dummy' },
 { oid => '2143',
   descr => 'minimum value of all timestamp with time zone input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f',
+  proname => 'min', proisagg => 't', proisstrict => 'f',
   prorettype => 'timestamptz', proargtypes => 'timestamptz',
   prosrc => 'aggregate_dummy' },
 { oid => '2144', descr => 'minimum value of all interval input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f',
+  proname => 'min', proisagg => 't', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
 { oid => '2145', descr => 'minimum value of all text values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'text',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'text',
   proargtypes => 'text', prosrc => 'aggregate_dummy' },
 { oid => '2146', descr => 'minimum value of all numeric input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2051', descr => 'minimum value of all anyarray input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f',
+  proname => 'min', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
   prosrc => 'aggregate_dummy' },
 { oid => '2245', descr => 'minimum value of all bpchar input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'bpchar',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'bpchar',
   proargtypes => 'bpchar', prosrc => 'aggregate_dummy' },
 { oid => '2798', descr => 'minimum value of all tid input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'tid',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'tid',
   proargtypes => 'tid', prosrc => 'aggregate_dummy' },
 { oid => '3565', descr => 'minimum value of all inet input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'inet',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'inet',
   proargtypes => 'inet', prosrc => 'aggregate_dummy' },
 
 # count has two forms: count(any) and count(*)
 { oid => '2147',
   descr => 'number of input rows for which the input expression is not null',
-  proname => 'count', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'count', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'any', prosrc => 'aggregate_dummy' },
 { oid => '2803', descr => 'number of input rows',
-  proname => 'count', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'count', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => '', prosrc => 'aggregate_dummy' },
 
 { oid => '2718',
   descr => 'population variance of bigint input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2719',
   descr => 'population variance of integer input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2720',
   descr => 'population variance of smallint input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2721',
   descr => 'population variance of float4 input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 { oid => '2722',
   descr => 'population variance of float8 input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2723',
   descr => 'population variance of numeric input values (square of the population standard deviation)',
-  proname => 'var_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'var_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2641',
   descr => 'sample variance of bigint input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2642',
   descr => 'sample variance of integer input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2643',
   descr => 'sample variance of smallint input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2644',
   descr => 'sample variance of float4 input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2645',
   descr => 'sample variance of float8 input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2646',
   descr => 'sample variance of numeric input values (square of the sample standard deviation)',
-  proname => 'var_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'var_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2148', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2149', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2150', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2151', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 { oid => '2152', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2153', descr => 'historical alias for var_samp',
-  proname => 'variance', prokind => 'a', proisstrict => 'f',
+  proname => 'variance', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2724',
   descr => 'population standard deviation of bigint input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2725',
   descr => 'population standard deviation of integer input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2726',
   descr => 'population standard deviation of smallint input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2727',
   descr => 'population standard deviation of float4 input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 { oid => '2728',
   descr => 'population standard deviation of float8 input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2729',
   descr => 'population standard deviation of numeric input values',
-  proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2712', descr => 'sample standard deviation of bigint input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2713', descr => 'sample standard deviation of integer input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2714',
   descr => 'sample standard deviation of smallint input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2715', descr => 'sample standard deviation of float4 input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 { oid => '2716', descr => 'sample standard deviation of float8 input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2717', descr => 'sample standard deviation of numeric input values',
-  proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2154', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2155', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2156', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2157', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 { oid => '2158', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2159', descr => 'historical alias for stddev_samp',
-  proname => 'stddev', prokind => 'a', proisstrict => 'f',
+  proname => 'stddev', proisagg => 't', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2818',
   descr => 'number of input rows in which both expressions are not null',
-  proname => 'regr_count', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_count', proisagg => 't', proisstrict => 'f',
   prorettype => 'int8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2819',
   descr => 'sum of squares of the independent variable (sum(X^2) - sum(X)^2/N)',
-  proname => 'regr_sxx', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_sxx', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2820',
   descr => 'sum of squares of the dependent variable (sum(Y^2) - sum(Y)^2/N)',
-  proname => 'regr_syy', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_syy', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2821',
   descr => 'sum of products of independent times dependent variable (sum(X*Y) - sum(X) * sum(Y)/N)',
-  proname => 'regr_sxy', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_sxy', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2822', descr => 'average of the independent variable (sum(X)/N)',
-  proname => 'regr_avgx', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_avgx', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2823', descr => 'average of the dependent variable (sum(Y)/N)',
-  proname => 'regr_avgy', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_avgy', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2824', descr => 'square of the correlation coefficient',
-  proname => 'regr_r2', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_r2', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2825',
   descr => 'slope of the least-squares-fit linear equation determined by the (X, Y) pairs',
-  proname => 'regr_slope', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_slope', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2826',
   descr => 'y-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs',
-  proname => 'regr_intercept', prokind => 'a', proisstrict => 'f',
+  proname => 'regr_intercept', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 
 { oid => '2827', descr => 'population covariance',
-  proname => 'covar_pop', prokind => 'a', proisstrict => 'f',
+  proname => 'covar_pop', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2828', descr => 'sample covariance',
-  proname => 'covar_samp', prokind => 'a', proisstrict => 'f',
+  proname => 'covar_samp', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '2829', descr => 'correlation coefficient',
-  proname => 'corr', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
+  proname => 'corr', proisagg => 't', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' },
 
 { oid => '2160',
@@ -7731,41 +7731,41 @@
   proname => 'bool_anytrue', prorettype => 'bool', proargtypes => 'internal',
   prosrc => 'bool_anytrue' },
 { oid => '2517', descr => 'boolean-and aggregate',
-  proname => 'bool_and', prokind => 'a', proisstrict => 'f',
+  proname => 'bool_and', proisagg => 't', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'bool', prosrc => 'aggregate_dummy' },
 
 # ANY, SOME? These names conflict with subquery operators. See doc.
 { oid => '2518', descr => 'boolean-or aggregate',
-  proname => 'bool_or', prokind => 'a', proisstrict => 'f',
+  proname => 'bool_or', proisagg => 't', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'bool', prosrc => 'aggregate_dummy' },
 { oid => '2519', descr => 'boolean-and aggregate',
-  proname => 'every', prokind => 'a', proisstrict => 'f', prorettype => 'bool',
+  proname => 'every', proisagg => 't', proisstrict => 'f', prorettype => 'bool',
   proargtypes => 'bool', prosrc => 'aggregate_dummy' },
 
 # bitwise integer aggregates
 { oid => '2236', descr => 'bitwise-and smallint aggregate',
-  proname => 'bit_and', prokind => 'a', proisstrict => 'f',
+  proname => 'bit_and', proisagg => 't', proisstrict => 'f',
   prorettype => 'int2', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2237', descr => 'bitwise-or smallint aggregate',
-  proname => 'bit_or', prokind => 'a', proisstrict => 'f', prorettype => 'int2',
+  proname => 'bit_or', proisagg => 't', proisstrict => 'f', prorettype => 'int2',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2238', descr => 'bitwise-and integer aggregate',
-  proname => 'bit_and', prokind => 'a', proisstrict => 'f',
+  proname => 'bit_and', proisagg => 't', proisstrict => 'f',
   prorettype => 'int4', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2239', descr => 'bitwise-or integer aggregate',
-  proname => 'bit_or', prokind => 'a', proisstrict => 'f', prorettype => 'int4',
+  proname => 'bit_or', proisagg => 't', proisstrict => 'f', prorettype => 'int4',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2240', descr => 'bitwise-and bigint aggregate',
-  proname => 'bit_and', prokind => 'a', proisstrict => 'f',
+  proname => 'bit_and', proisagg => 't', proisstrict => 'f',
   prorettype => 'int8', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2241', descr => 'bitwise-or bigint aggregate',
-  proname => 'bit_or', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
+  proname => 'bit_or', proisagg => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2242', descr => 'bitwise-and bit aggregate',
-  proname => 'bit_and', prokind => 'a', proisstrict => 'f', prorettype => 'bit',
+  proname => 'bit_and', proisagg => 't', proisstrict => 'f', prorettype => 'bit',
   proargtypes => 'bit', prosrc => 'aggregate_dummy' },
 { oid => '2243', descr => 'bitwise-or bit aggregate',
-  proname => 'bit_or', prokind => 'a', proisstrict => 'f', prorettype => 'bit',
+  proname => 'bit_or', proisagg => 't', proisstrict => 'f', prorettype => 'bit',
   proargtypes => 'bit', prosrc => 'aggregate_dummy' },
 
 # formerly-missing interval + datetime operators
@@ -8076,7 +8076,7 @@
   proname => 'xmlconcat2', proisstrict => 'f', prorettype => 'xml',
   proargtypes => 'xml xml', prosrc => 'xmlconcat2' },
 { oid => '2901', descr => 'concatenate XML values',
-  proname => 'xmlagg', prokind => 'a', proisstrict => 'f', prorettype => 'xml',
+  proname => 'xmlagg', proisagg => 't', proisstrict => 'f', prorettype => 'xml',
   proargtypes => 'xml', prosrc => 'aggregate_dummy' },
 { oid => '2922', descr => 'serialize an XML value to a character string',
   proname => 'text', prorettype => 'text', proargtypes => 'xml',
@@ -8228,7 +8228,7 @@
   proname => 'json_agg_finalfn', proisstrict => 'f', prorettype => 'json',
   proargtypes => 'internal', prosrc => 'json_agg_finalfn' },
 { oid => '3175', descr => 'aggregate input into json',
-  proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
+  proname => 'json_agg', proisagg => 't', proisstrict => 'f', provolatile => 's',
   prorettype => 'json', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
 { oid => '3180', descr => 'json object aggregate transition function',
@@ -8240,7 +8240,7 @@
   prorettype => 'json', proargtypes => 'internal',
   prosrc => 'json_object_agg_finalfn' },
 { oid => '3197', descr => 'aggregate input into a json object',
-  proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'json_object_agg', proisagg => 't', proisstrict => 'f',
   provolatile => 's', prorettype => 'json', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
 { oid => '3198', descr => 'build a json array from any inputs',
@@ -8483,10 +8483,10 @@
   proname => 'enum_larger', prorettype => 'anyenum',
   proargtypes => 'anyenum anyenum', prosrc => 'enum_larger' },
 { oid => '3526', descr => 'maximum value of all enum input values',
-  proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'anyenum',
+  proname => 'max', proisagg => 't', proisstrict => 'f', prorettype => 'anyenum',
   proargtypes => 'anyenum', prosrc => 'aggregate_dummy' },
 { oid => '3527', descr => 'minimum value of all enum input values',
-  proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'anyenum',
+  proname => 'min', proisagg => 't', proisstrict => 'f', prorettype => 'anyenum',
   proargtypes => 'anyenum', prosrc => 'aggregate_dummy' },
 { oid => '3528', descr => 'first value of the input enum type',
   proname => 'enum_first', proisstrict => 'f', provolatile => 's',
@@ -9082,7 +9082,7 @@
   prorettype => 'jsonb', proargtypes => 'internal',
   prosrc => 'jsonb_agg_finalfn' },
 { oid => '3267', descr => 'aggregate input into jsonb',
-  proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'jsonb_agg', proisagg => 't', proisstrict => 'f',
   provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
 { oid => '3268', descr => 'jsonb object aggregate transition function',
@@ -9094,7 +9094,7 @@
   prorettype => 'jsonb', proargtypes => 'internal',
   prosrc => 'jsonb_object_agg_finalfn' },
 { oid => '3270', descr => 'aggregate inputs into jsonb object',
-  proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
+  proname => 'jsonb_object_agg', proisagg => 't', proisstrict => 'f',
   prorettype => 'jsonb', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
 { oid => '3271', descr => 'build a jsonb array from any inputs',
@@ -9412,51 +9412,51 @@
 
 # SQL-spec window functions
 { oid => '3100', descr => 'row number within partition',
-  proname => 'row_number', prokind => 'w', proisstrict => 'f',
+  proname => 'row_number', proiswindow => 't', proisstrict => 'f',
   prorettype => 'int8', proargtypes => '', prosrc => 'window_row_number' },
 { oid => '3101', descr => 'integer rank with gaps',
-  proname => 'rank', prokind => 'w', proisstrict => 'f', prorettype => 'int8',
+  proname => 'rank', proiswindow => 't', proisstrict => 'f', prorettype => 'int8',
   proargtypes => '', prosrc => 'window_rank' },
 { oid => '3102', descr => 'integer rank without gaps',
-  proname => 'dense_rank', prokind => 'w', proisstrict => 'f',
+  proname => 'dense_rank', proiswindow => 't', proisstrict => 'f',
   prorettype => 'int8', proargtypes => '', prosrc => 'window_dense_rank' },
 { oid => '3103', descr => 'fractional rank within partition',
-  proname => 'percent_rank', prokind => 'w', proisstrict => 'f',
+  proname => 'percent_rank', proiswindow => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => '', prosrc => 'window_percent_rank' },
 { oid => '3104', descr => 'fractional row number within partition',
-  proname => 'cume_dist', prokind => 'w', proisstrict => 'f',
+  proname => 'cume_dist', proiswindow => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => '', prosrc => 'window_cume_dist' },
 { oid => '3105', descr => 'split rows into N groups',
-  proname => 'ntile', prokind => 'w', prorettype => 'int4',
+  proname => 'ntile', proiswindow => 't', prorettype => 'int4',
   proargtypes => 'int4', prosrc => 'window_ntile' },
 { oid => '3106', descr => 'fetch the preceding row value',
-  proname => 'lag', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lag', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement', prosrc => 'window_lag' },
 { oid => '3107', descr => 'fetch the Nth preceding row value',
-  proname => 'lag', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lag', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement int4', prosrc => 'window_lag_with_offset' },
 { oid => '3108', descr => 'fetch the Nth preceding row value with default',
-  proname => 'lag', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lag', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement int4 anyelement',
   prosrc => 'window_lag_with_offset_and_default' },
 { oid => '3109', descr => 'fetch the following row value',
-  proname => 'lead', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lead', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement', prosrc => 'window_lead' },
 { oid => '3110', descr => 'fetch the Nth following row value',
-  proname => 'lead', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lead', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement int4', prosrc => 'window_lead_with_offset' },
 { oid => '3111', descr => 'fetch the Nth following row value with default',
-  proname => 'lead', prokind => 'w', prorettype => 'anyelement',
+  proname => 'lead', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement int4 anyelement',
   prosrc => 'window_lead_with_offset_and_default' },
 { oid => '3112', descr => 'fetch the first row value',
-  proname => 'first_value', prokind => 'w', prorettype => 'anyelement',
+  proname => 'first_value', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement', prosrc => 'window_first_value' },
 { oid => '3113', descr => 'fetch the last row value',
-  proname => 'last_value', prokind => 'w', prorettype => 'anyelement',
+  proname => 'last_value', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement', prosrc => 'window_last_value' },
 { oid => '3114', descr => 'fetch the Nth row value',
-  proname => 'nth_value', prokind => 'w', prorettype => 'anyelement',
+  proname => 'nth_value', proiswindow => 't', prorettype => 'anyelement',
   proargtypes => 'anyelement int4', prosrc => 'window_nth_value' },
 
 # functions for range types
@@ -9899,7 +9899,7 @@
 
 # inverse distribution aggregates (and their support functions)
 { oid => '3972', descr => 'discrete percentile',
-  proname => 'percentile_disc', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_disc', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyelement', proargtypes => 'float8 anyelement',
   prosrc => 'aggregate_dummy' },
 { oid => '3973', descr => 'aggregate final function',
@@ -9907,7 +9907,7 @@
   prorettype => 'anyelement', proargtypes => 'internal float8 anyelement',
   prosrc => 'percentile_disc_final' },
 { oid => '3974', descr => 'continuous distribution percentile',
-  proname => 'percentile_cont', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_cont', proisagg => 't', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '3975', descr => 'aggregate final function',
@@ -9915,7 +9915,7 @@
   prorettype => 'float8', proargtypes => 'internal float8',
   prosrc => 'percentile_cont_float8_final' },
 { oid => '3976', descr => 'continuous distribution percentile',
-  proname => 'percentile_cont', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_cont', proisagg => 't', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'float8 interval',
   prosrc => 'aggregate_dummy' },
 { oid => '3977', descr => 'aggregate final function',
@@ -9923,7 +9923,7 @@
   prorettype => 'interval', proargtypes => 'internal float8',
   prosrc => 'percentile_cont_interval_final' },
 { oid => '3978', descr => 'multiple discrete percentiles',
-  proname => 'percentile_disc', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_disc', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => '_float8 anyelement',
   prosrc => 'aggregate_dummy' },
 { oid => '3979', descr => 'aggregate final function',
@@ -9931,7 +9931,7 @@
   prorettype => 'anyarray', proargtypes => 'internal _float8 anyelement',
   prosrc => 'percentile_disc_multi_final' },
 { oid => '3980', descr => 'multiple continuous percentiles',
-  proname => 'percentile_cont', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_cont', proisagg => 't', proisstrict => 'f',
   prorettype => '_float8', proargtypes => '_float8 float8',
   prosrc => 'aggregate_dummy' },
 { oid => '3981', descr => 'aggregate final function',
@@ -9939,7 +9939,7 @@
   prorettype => '_float8', proargtypes => 'internal _float8',
   prosrc => 'percentile_cont_float8_multi_final' },
 { oid => '3982', descr => 'multiple continuous percentiles',
-  proname => 'percentile_cont', prokind => 'a', proisstrict => 'f',
+  proname => 'percentile_cont', proisagg => 't', proisstrict => 'f',
   prorettype => '_interval', proargtypes => '_float8 interval',
   prosrc => 'aggregate_dummy' },
 { oid => '3983', descr => 'aggregate final function',
@@ -9947,7 +9947,7 @@
   prorettype => '_interval', proargtypes => 'internal _float8',
   prosrc => 'percentile_cont_interval_multi_final' },
 { oid => '3984', descr => 'most common value',
-  proname => 'mode', prokind => 'a', proisstrict => 'f',
+  proname => 'mode', proisagg => 't', proisstrict => 'f',
   prorettype => 'anyelement', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
 { oid => '3985', descr => 'aggregate final function',
@@ -9956,7 +9956,7 @@
 
 # hypothetical-set aggregates (and their support functions)
 { oid => '3986', descr => 'rank of hypothetical row',
-  proname => 'rank', provariadic => 'any', prokind => 'a', proisstrict => 'f',
+  proname => 'rank', provariadic => 'any', proisagg => 't', proisstrict => 'f',
   prorettype => 'int8', proargtypes => 'any', proallargtypes => '{any}',
   proargmodes => '{v}', prosrc => 'aggregate_dummy' },
 { oid => '3987', descr => 'aggregate final function',
@@ -9965,7 +9965,7 @@
   proallargtypes => '{internal,any}', proargmodes => '{i,v}',
   prosrc => 'hypothetical_rank_final' },
 { oid => '3988', descr => 'fractional rank of hypothetical row',
-  proname => 'percent_rank', provariadic => 'any', prokind => 'a',
+  proname => 'percent_rank', provariadic => 'any', proisagg => 't',
   proisstrict => 'f', prorettype => 'float8', proargtypes => 'any',
   proallargtypes => '{any}', proargmodes => '{v}',
   prosrc => 'aggregate_dummy' },
@@ -9975,7 +9975,7 @@
   proallargtypes => '{internal,any}', proargmodes => '{i,v}',
   prosrc => 'hypothetical_percent_rank_final' },
 { oid => '3990', descr => 'cumulative distribution of hypothetical row',
-  proname => 'cume_dist', provariadic => 'any', prokind => 'a',
+  proname => 'cume_dist', provariadic => 'any', proisagg => 't',
   proisstrict => 'f', prorettype => 'float8', proargtypes => 'any',
   proallargtypes => '{any}', proargmodes => '{v}',
   prosrc => 'aggregate_dummy' },
@@ -9985,7 +9985,7 @@
   proallargtypes => '{internal,any}', proargmodes => '{i,v}',
   prosrc => 'hypothetical_cume_dist_final' },
 { oid => '3992', descr => 'rank of hypothetical row without gaps',
-  proname => 'dense_rank', provariadic => 'any', prokind => 'a',
+  proname => 'dense_rank', provariadic => 'any', proisagg => 't',
   proisstrict => 'f', prorettype => 'int8', proargtypes => 'any',
   proallargtypes => '{any}', proargmodes => '{v}',
   prosrc => 'aggregate_dummy' },
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a34b2596fa..8b31d47a77 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -57,6 +57,12 @@ CATALOG(pg_proc,1255,ProcedureRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81,Proce
 	/* see PROKIND_ categories below */
 	char		prokind BKI_DEFAULT(f);
 
+	/* is it an aggregate? */
+	bool		proisagg BKI_DEFAULT(f);
+
+	/* is it a window function? */
+	bool		proiswindow BKI_DEFAULT(f);
+
 	/* security definer */
 	bool		prosecdef BKI_DEFAULT(f);
 
@@ -136,9 +142,8 @@ typedef FormData_pg_proc *Form_pg_proc;
 /*
  * Symbolic values for prokind column
  */
+#define PROKIND_ANY '\0'
 #define PROKIND_FUNCTION 'f'
-#define PROKIND_AGGREGATE 'a'
-#define PROKIND_WINDOW 'w'
 #define PROKIND_PROCEDURE 'p'
 
 /*
@@ -187,6 +192,8 @@ extern ObjectAddress ProcedureCreate(const char *procedureName,
 				const char *prosrc,
 				const char *probin,
 				char prokind,
+				bool isAgg,
+				bool isWindowFunc,
 				bool security_definer,
 				bool isLeakProof,
 				bool isStrict,
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 8fc9e424cf..a3e8c2253d 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -60,8 +60,8 @@ extern ObjectAddress CreateCast(CreateCastStmt *stmt);
 extern void DropCastById(Oid castOid);
 extern ObjectAddress CreateTransform(CreateTransformStmt *stmt);
 extern void DropTransformById(Oid transformOid);
-extern void IsThereFunctionInNamespace(const char *proname, int pronargs,
-						   oidvector *proargtypes, Oid nspOid);
+extern void IsThereFunctionInNamespace(const char *proname, char prokind, int pronargs,
+									   oidvector *proargtypes, Oid nspOid);
 extern void ExecuteDoStmt(DoStmt *stmt, bool atomic);
 extern void ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver *dest);
 extern Oid	get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 11f9046e38..7380d3abed 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -23,8 +23,7 @@ typedef enum
 {
 	FUNCDETAIL_NOTFOUND,		/* no matching function */
 	FUNCDETAIL_MULTIPLE,		/* too many matching functions */
-	FUNCDETAIL_NORMAL,			/* found a matching regular function */
-	FUNCDETAIL_PROCEDURE,		/* found a matching procedure */
+	FUNCDETAIL_NORMAL,			/* found a matching regular function or procedure */
 	FUNCDETAIL_AGGREGATE,		/* found a matching aggregate function */
 	FUNCDETAIL_WINDOWFUNC,		/* found a matching window function */
 	FUNCDETAIL_COERCION			/* it's a type coercion request */
@@ -32,10 +31,11 @@ typedef enum
 
 
 extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
-				  Node *last_srf, FuncCall *fn, bool proc_call,
+				  Node *last_srf, FuncCall *fn, char prokind,
 				  int location);
 
 extern FuncDetailCode func_get_detail(List *funcname,
+				char prokind,
 				List *fargs, List *fargnames,
 				int nargs, Oid *argtypes,
 				bool expand_variadic, bool expand_defaults,
@@ -62,7 +62,7 @@ extern const char *funcname_signature_string(const char *funcname, int nargs,
 extern const char *func_signature_string(List *funcname, int nargs,
 					  List *argnames, const Oid *argtypes);
 
-extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
+extern Oid LookupFuncName(List *funcname, char prokind, int nargs, const Oid *argtypes,
 			   bool noError);
 extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
 				   bool noError);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index e55ea4035b..1f6c04a8f3 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -117,7 +117,7 @@ extern bool get_func_retset(Oid funcid);
 extern bool func_strict(Oid funcid);
 extern char func_volatile(Oid funcid);
 extern char func_parallel(Oid funcid);
-extern char get_func_prokind(Oid funcid);
+extern bool get_func_isagg(Oid funcid);
 extern bool get_func_leakproof(Oid funcid);
 extern float4 get_func_cost(Oid funcid);
 extern float4 get_func_rows(Oid funcid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 4f333586ee..b8adc9d6d7 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -73,7 +73,7 @@ enum SysCacheIdentifier
 	OPFAMILYAMNAMENSP,
 	OPFAMILYOID,
 	PARTRELID,
-	PROCNAMEARGSNSP,
+	PROCNAMEARGSNSPKIND,
 	PROCOID,
 	PUBLICATIONNAME,
 	PUBLICATIONOID,
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 12f7b13780..42da52b9ca 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -620,7 +620,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 
 	/* Parse possibly-qualified identifier and look up the function */
 	namelist = stringToQualifiedNameList(start_proc);
-	procOid = LookupFuncName(namelist, 0, fargtypes, false);
+	procOid = LookupFuncName(namelist, PROKIND_FUNCTION, 0, fargtypes, false);
 
 	/* Current user must have permission to call function */
 	aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index f24a17f40e..e135c6a0ef 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -83,21 +83,21 @@ ERROR:  must be owner of function alt_agg3
 ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2;  -- failed (name conflict)
 ERROR:  function alt_agg2(integer) already exists in schema "alt_nsp2"
 RESET SESSION AUTHORIZATION;
-SELECT n.nspname, proname, prorettype::regtype, prokind, a.rolname
+SELECT n.nspname, proname, prorettype::regtype, proisagg, a.rolname
   FROM pg_proc p, pg_namespace n, pg_authid a
   WHERE p.pronamespace = n.oid AND p.proowner = a.oid
     AND n.nspname IN ('alt_nsp1', 'alt_nsp2')
   ORDER BY nspname, proname;
- nspname  |  proname  | prorettype | prokind |           rolname           
-----------+-----------+------------+---------+-----------------------------
- alt_nsp1 | alt_agg2  | integer    | a       | regress_alter_generic_user2
- alt_nsp1 | alt_agg3  | integer    | a       | regress_alter_generic_user1
- alt_nsp1 | alt_agg4  | integer    | a       | regress_alter_generic_user2
- alt_nsp1 | alt_func2 | integer    | f       | regress_alter_generic_user2
- alt_nsp1 | alt_func3 | integer    | f       | regress_alter_generic_user1
- alt_nsp1 | alt_func4 | integer    | f       | regress_alter_generic_user2
- alt_nsp2 | alt_agg2  | integer    | a       | regress_alter_generic_user3
- alt_nsp2 | alt_func2 | integer    | f       | regress_alter_generic_user3
+ nspname  |  proname  | prorettype | proisagg |           rolname           
+----------+-----------+------------+----------+-----------------------------
+ alt_nsp1 | alt_agg2  | integer    | t        | regress_alter_generic_user2
+ alt_nsp1 | alt_agg3  | integer    | t        | regress_alter_generic_user1
+ alt_nsp1 | alt_agg4  | integer    | t        | regress_alter_generic_user2
+ alt_nsp1 | alt_func2 | integer    | f        | regress_alter_generic_user2
+ alt_nsp1 | alt_func3 | integer    | f        | regress_alter_generic_user1
+ alt_nsp1 | alt_func4 | integer    | f        | regress_alter_generic_user2
+ alt_nsp2 | alt_agg2  | integer    | t        | regress_alter_generic_user3
+ alt_nsp2 | alt_func2 | integer    | f        | regress_alter_generic_user3
 (8 rows)
 
 --
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 3301885fc8..76de12be52 100644
--- a/src/test/regress/expected/create_function_3.out
+++ b/src/test/regress/expected/create_function_3.out
@@ -278,12 +278,12 @@ HINT:  Specify the argument list to select the function unambiguously.
 -- CREATE OR REPLACE tests
 CREATE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL AS 'SELECT $1';
 CREATE OR REPLACE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1';
-ERROR:  cannot change routine kind
-DETAIL:  "functest1" is a function.
-CREATE OR REPLACE PROCEDURE functest1(a int) LANGUAGE SQL AS 'SELECT $1';
-ERROR:  cannot change routine kind
-DETAIL:  "functest1" is a function.
+ERROR:  function "functest1" is not a window function
+CREATE FUNCTION functest2(a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1';
+CREATE OR REPLACE FUNCTION functest2(a int) RETURNS int LANGUAGE SQL AS 'SELECT $1';
+ERROR:  function "functest2" is a window function
 DROP FUNCTION functest1(a int);
+DROP FUNCTION functest2(a int);
 -- Check behavior of VOID-returning SQL functions
 CREATE FUNCTION voidtest1(a int) RETURNS VOID LANGUAGE SQL AS
 $$ SELECT a + 1 $$;
diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out
index 67d671727c..3305e2330f 100644
--- a/src/test/regress/expected/create_procedure.out
+++ b/src/test/regress/expected/create_procedure.out
@@ -1,13 +1,13 @@
 CALL nonexistent();  -- error
-ERROR:  function nonexistent() does not exist
+ERROR:  procedure nonexistent() does not exist
 LINE 1: CALL nonexistent();
              ^
-HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+HINT:  No procedure matches the given name and argument types. You might need to add explicit type casts.
 CALL random();  -- error
-ERROR:  random() is not a procedure
+ERROR:  procedure random() does not exist
 LINE 1: CALL random();
              ^
-HINT:  To call a function, use SELECT.
+HINT:  No procedure matches the given name and argument types. You might need to add explicit type casts.
 CREATE FUNCTION cp_testfunc1(a int) RETURNS int LANGUAGE SQL AS $$ SELECT a $$;
 CREATE TABLE cp_test (a int, b text);
 CREATE PROCEDURE ptest1(x text)
@@ -16,10 +16,10 @@ AS $$
 INSERT INTO cp_test VALUES (1, x);
 $$;
 SELECT ptest1('x');  -- error
-ERROR:  ptest1(unknown) is a procedure
+ERROR:  function ptest1(unknown) does not exist
 LINE 1: SELECT ptest1('x');
                ^
-HINT:  To call a procedure, use CALL.
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 CALL ptest1('a');  -- ok
 CALL ptest1('xy' || 'zzy');  -- ok, constant-folded arg
 CALL ptest1(substring(random()::numeric(20,15)::text, 1, 1));  -- ok, volatile arg
@@ -118,14 +118,15 @@ SELECT * FROM cp_test;
 
 -- various error cases
 CALL version();  -- error: not a procedure
-ERROR:  version() is not a procedure
+ERROR:  procedure version() does not exist
 LINE 1: CALL version();
              ^
-HINT:  To call a function, use SELECT.
+HINT:  No procedure matches the given name and argument types. You might need to add explicit type casts.
 CALL sum(1);  -- error: not a procedure
-ERROR:  sum(integer) is not a procedure
+ERROR:  procedure sum(integer) does not exist
 LINE 1: CALL sum(1);
              ^
+HINT:  No procedure matches the given name and argument types. You might need to add explicit type casts.
 CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
 ERROR:  invalid attribute in procedure definition
 LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT I...
@@ -142,15 +143,15 @@ ERROR:  invalid attribute in procedure definition
 LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
                                      ^
 ALTER FUNCTION ptest1(text) VOLATILE;  -- error: not a function
-ERROR:  ptest1(text) is not a function
+ERROR:  function ptest1(text) does not exist
 ALTER PROCEDURE cp_testfunc1(int) VOLATILE;  -- error: not a procedure
-ERROR:  cp_testfunc1(integer) is not a procedure
+ERROR:  procedure cp_testfunc1(integer) does not exist
 ALTER PROCEDURE nonexistent() VOLATILE;
 ERROR:  procedure nonexistent() does not exist
 DROP FUNCTION ptest1(text);  -- error: not a function
-ERROR:  ptest1(text) is not a function
+ERROR:  function ptest1(text) does not exist
 DROP PROCEDURE cp_testfunc1(int);  -- error: not a procedure
-ERROR:  cp_testfunc1(integer) is not a procedure
+ERROR:  procedure cp_testfunc1(integer) does not exist
 DROP PROCEDURE nonexistent();
 ERROR:  procedure nonexistent() does not exist
 -- privileges
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a1e18a6ceb..a97bb457fd 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -74,7 +74,7 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR
        0::oid = ANY (p1.proargtypes) OR
        procost <= 0 OR
        CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END OR
-       prokind NOT IN ('f', 'a', 'w', 'p') OR
+       prokind NOT IN ('f', 'p') OR
        provolatile NOT IN ('i', 's', 'v') OR
        proparallel NOT IN ('s', 'r', 'u');
  oid | proname 
@@ -89,10 +89,10 @@ WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-';
 -----+---------
 (0 rows)
 
--- proretset should only be set for normal functions
+-- proiswindow shouldn't be set together with proisagg or proretset
 SELECT p1.oid, p1.proname
 FROM pg_proc AS p1
-WHERE proretset AND prokind != 'f';
+WHERE proiswindow AND (proisagg OR proretset);
  oid | proname 
 -----+---------
 (0 rows)
@@ -155,9 +155,9 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid < p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    (p1.prokind != 'a' OR p2.prokind != 'a') AND
+    (NOT p1.proisagg OR NOT p2.proisagg) AND
     (p1.prolang != p2.prolang OR
-     p1.prokind != p2.prokind OR
+     p1.proisagg != p2.proisagg OR
      p1.prosecdef != p2.prosecdef OR
      p1.proleakproof != p2.proleakproof OR
      p1.proisstrict != p2.proisstrict OR
@@ -183,7 +183,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.prorettype < p2.prorettype)
@@ -199,7 +199,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
@@ -217,7 +217,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
@@ -234,7 +234,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[2] < p2.proargtypes[2])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -247,7 +247,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[3] < p2.proargtypes[3])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -260,7 +260,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[4] < p2.proargtypes[4])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -272,7 +272,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[5] < p2.proargtypes[5])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -284,7 +284,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[6] < p2.proargtypes[6])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -296,7 +296,7 @@ FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[7] < p2.proargtypes[7])
 ORDER BY 1, 2;
  proargtypes | proargtypes 
@@ -1294,15 +1294,15 @@ WHERE aggfnoid = 0 OR aggtransfn = 0 OR
 SELECT a.aggfnoid::oid, p.proname
 FROM pg_aggregate as a, pg_proc as p
 WHERE a.aggfnoid = p.oid AND
-    (p.prokind != 'a' OR p.proretset OR p.pronargs < a.aggnumdirectargs);
+    (NOT p.proisagg OR p.proretset OR p.pronargs < a.aggnumdirectargs);
  aggfnoid | proname 
 ----------+---------
 (0 rows)
 
--- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches.
+-- Make sure there are no proisagg pg_proc entries without matches.
 SELECT oid, proname
 FROM pg_proc as p
-WHERE p.prokind = 'a' AND
+WHERE p.proisagg AND
     NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid);
  oid | proname 
 -----+---------
@@ -1641,7 +1641,7 @@ ORDER BY 1, 2;
 SELECT p1.oid::regprocedure, p2.oid::regprocedure
 FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND
-    p1.prokind = 'a' AND p2.prokind = 'a' AND
+    p1.proisagg AND p2.proisagg AND
     array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
 ORDER BY 1;
      oid      |   oid   
@@ -1652,7 +1652,7 @@ ORDER BY 1;
 -- For the same reason, built-in aggregates with default arguments are no good.
 SELECT oid, proname
 FROM pg_proc AS p
-WHERE prokind = 'a' AND proargdefaults IS NOT NULL;
+WHERE proisagg AND proargdefaults IS NOT NULL;
  oid | proname 
 -----+---------
 (0 rows)
@@ -1662,7 +1662,7 @@ WHERE prokind = 'a' AND proargdefaults IS NOT NULL;
 -- that is not subject to the misplaced ORDER BY issue).
 SELECT p.oid, proname
 FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
-WHERE prokind = 'a' AND provariadic != 0 AND a.aggkind = 'n';
+WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
  oid | proname 
 -----+---------
 (0 rows)
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index ac8968d24f..4a433a8d1b 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -656,7 +656,7 @@ CREATE PROCEDURE priv_testproc1(int) AS 'select $1;' LANGUAGE sql;
 REVOKE ALL ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION priv_testfunc1(int), priv_testfunc2(int), priv_testagg1(int) TO regress_priv_user2;
 REVOKE ALL ON FUNCTION priv_testproc1(int) FROM PUBLIC; -- fail, not a function
-ERROR:  priv_testproc1(integer) is not a function
+ERROR:  function priv_testproc1(integer) does not exist
 REVOKE ALL ON PROCEDURE priv_testproc1(int) FROM PUBLIC;
 GRANT EXECUTE ON PROCEDURE priv_testproc1(int) TO regress_priv_user2;
 GRANT USAGE ON FUNCTION priv_testfunc1(int) TO regress_priv_user3; -- semantic error
diff --git a/src/test/regress/expected/regex.out b/src/test/regress/expected/regex.out
index 79a7fa7a84..b28256ef24 100644
--- a/src/test/regress/expected/regex.out
+++ b/src/test/regress/expected/regex.out
@@ -298,15 +298,15 @@ explain (costs off) select * from pg_proc where proname ~ 'abc';
 explain (costs off) select * from pg_proc where proname ~ '^abc';
                               QUERY PLAN                              
 ----------------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: ((proname >= 'abc'::name) AND (proname < 'abd'::name))
    Filter: (proname ~ '^abc'::text)
 (3 rows)
 
 explain (costs off) select * from pg_proc where proname ~ '^abc$';
-                         QUERY PLAN                         
-------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+                           QUERY PLAN                            
+-----------------------------------------------------------------
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: (proname = 'abc'::name)
    Filter: (proname ~ '^abc$'::text)
 (3 rows)
@@ -314,7 +314,7 @@ explain (costs off) select * from pg_proc where proname ~ '^abc$';
 explain (costs off) select * from pg_proc where proname ~ '^abcd*e';
                               QUERY PLAN                              
 ----------------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: ((proname >= 'abc'::name) AND (proname < 'abd'::name))
    Filter: (proname ~ '^abcd*e'::text)
 (3 rows)
@@ -322,7 +322,7 @@ explain (costs off) select * from pg_proc where proname ~ '^abcd*e';
 explain (costs off) select * from pg_proc where proname ~ '^abc+d';
                               QUERY PLAN                              
 ----------------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: ((proname >= 'abc'::name) AND (proname < 'abd'::name))
    Filter: (proname ~ '^abc+d'::text)
 (3 rows)
@@ -330,15 +330,15 @@ explain (costs off) select * from pg_proc where proname ~ '^abc+d';
 explain (costs off) select * from pg_proc where proname ~ '^(abc)(def)';
                                  QUERY PLAN                                 
 ----------------------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: ((proname >= 'abcdef'::name) AND (proname < 'abcdeg'::name))
    Filter: (proname ~ '^(abc)(def)'::text)
 (3 rows)
 
 explain (costs off) select * from pg_proc where proname ~ '^(abc)$';
-                         QUERY PLAN                         
-------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+                           QUERY PLAN                            
+-----------------------------------------------------------------
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: (proname = 'abc'::name)
    Filter: (proname ~ '^(abc)$'::text)
 (3 rows)
@@ -353,7 +353,7 @@ explain (costs off) select * from pg_proc where proname ~ '^(abc)?d';
 explain (costs off) select * from pg_proc where proname ~ '^abcd(x|(?=\w\w)q)';
                                QUERY PLAN                               
 ------------------------------------------------------------------------
- Index Scan using pg_proc_proname_args_nsp_index on pg_proc
+ Index Scan using pg_proc_proname_args_nsp_kind_index on pg_proc
    Index Cond: ((proname >= 'abcd'::name) AND (proname < 'abce'::name))
    Filter: (proname ~ '^abcd(x|(?=\w\w)q)'::text)
 (3 rows)
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ae0cd253d5..aaddb8822d 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1521,12 +1521,10 @@ UNION ALL
  SELECT l.objoid,
     l.classoid,
     l.objsubid,
-        CASE pro.prokind
-            WHEN 'a'::"char" THEN 'aggregate'::text
-            WHEN 'f'::"char" THEN 'function'::text
-            WHEN 'p'::"char" THEN 'procedure'::text
-            WHEN 'w'::"char" THEN 'window'::text
-            ELSE NULL::text
+        CASE
+            WHEN (pro.prokind = 'p'::"char") THEN 'procedure'::text
+            WHEN pro.proisagg THEN 'aggregate'::text
+            ELSE 'function'::text
         END AS objtype,
     pro.pronamespace AS objnamespace,
     (((
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 84fd900b24..35172aad74 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -81,7 +81,7 @@ CREATE AGGREGATE alt_agg2 (
 
 RESET SESSION AUTHORIZATION;
 
-SELECT n.nspname, proname, prorettype::regtype, prokind, a.rolname
+SELECT n.nspname, proname, prorettype::regtype, proisagg, a.rolname
   FROM pg_proc p, pg_namespace n, pg_authid a
   WHERE p.pronamespace = n.oid AND p.proowner = a.oid
     AND n.nspname IN ('alt_nsp1', 'alt_nsp2')
diff --git a/src/test/regress/sql/create_function_3.sql b/src/test/regress/sql/create_function_3.sql
index 24bb900990..3f473f38c3 100644
--- a/src/test/regress/sql/create_function_3.sql
+++ b/src/test/regress/sql/create_function_3.sql
@@ -186,8 +186,10 @@ CREATE FUNCTION functest_B_2(bigint) RETURNS bool LANGUAGE 'sql'
 
 CREATE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL AS 'SELECT $1';
 CREATE OR REPLACE FUNCTION functest1(a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1';
-CREATE OR REPLACE PROCEDURE functest1(a int) LANGUAGE SQL AS 'SELECT $1';
+CREATE FUNCTION functest2(a int) RETURNS int LANGUAGE SQL WINDOW AS 'SELECT $1';
+CREATE OR REPLACE FUNCTION functest2(a int) RETURNS int LANGUAGE SQL AS 'SELECT $1';
 DROP FUNCTION functest1(a int);
+DROP FUNCTION functest2(a int);
 
 
 -- Check behavior of VOID-returning SQL functions
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index a593d37643..3302bebfe2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -82,7 +82,7 @@
        0::oid = ANY (p1.proargtypes) OR
        procost <= 0 OR
        CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END OR
-       prokind NOT IN ('f', 'a', 'w', 'p') OR
+       prokind NOT IN ('f', 'p') OR
        provolatile NOT IN ('i', 's', 'v') OR
        proparallel NOT IN ('s', 'r', 'u');
 
@@ -91,10 +91,10 @@
 FROM pg_proc as p1
 WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-';
 
--- proretset should only be set for normal functions
+-- proiswindow shouldn't be set together with proisagg or proretset
 SELECT p1.oid, p1.proname
 FROM pg_proc AS p1
-WHERE proretset AND prokind != 'f';
+WHERE proiswindow AND (proisagg OR proretset);
 
 -- currently, no built-in functions should be SECURITY DEFINER;
 -- this might change in future, but there will probably never be many.
@@ -141,9 +141,9 @@
 WHERE p1.oid < p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    (p1.prokind != 'a' OR p2.prokind != 'a') AND
+    (NOT p1.proisagg OR NOT p2.proisagg) AND
     (p1.prolang != p2.prolang OR
-     p1.prokind != p2.prokind OR
+     p1.proisagg != p2.proisagg OR
      p1.prosecdef != p2.prosecdef OR
      p1.proleakproof != p2.proleakproof OR
      p1.proisstrict != p2.proisstrict OR
@@ -167,7 +167,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.prorettype < p2.prorettype)
@@ -178,7 +178,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
@@ -189,7 +189,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
@@ -200,7 +200,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[2] < p2.proargtypes[2])
 ORDER BY 1, 2;
 
@@ -209,7 +209,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[3] < p2.proargtypes[3])
 ORDER BY 1, 2;
 
@@ -218,7 +218,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[4] < p2.proargtypes[4])
 ORDER BY 1, 2;
 
@@ -227,7 +227,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[5] < p2.proargtypes[5])
 ORDER BY 1, 2;
 
@@ -236,7 +236,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[6] < p2.proargtypes[6])
 ORDER BY 1, 2;
 
@@ -245,7 +245,7 @@
 WHERE p1.oid != p2.oid AND
     p1.prosrc = p2.prosrc AND
     p1.prolang = 12 AND p2.prolang = 12 AND
-    p1.prokind != 'a' AND p2.prokind != 'a' AND
+    NOT p1.proisagg AND NOT p2.proisagg AND
     (p1.proargtypes[7] < p2.proargtypes[7])
 ORDER BY 1, 2;
 
@@ -805,13 +805,13 @@
 SELECT a.aggfnoid::oid, p.proname
 FROM pg_aggregate as a, pg_proc as p
 WHERE a.aggfnoid = p.oid AND
-    (p.prokind != 'a' OR p.proretset OR p.pronargs < a.aggnumdirectargs);
+    (NOT p.proisagg OR p.proretset OR p.pronargs < a.aggnumdirectargs);
 
--- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches.
+-- Make sure there are no proisagg pg_proc entries without matches.
 
 SELECT oid, proname
 FROM pg_proc as p
-WHERE p.prokind = 'a' AND
+WHERE p.proisagg AND
     NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid);
 
 -- If there is no finalfn then the output type must be the transtype.
@@ -1090,7 +1090,7 @@
 SELECT p1.oid::regprocedure, p2.oid::regprocedure
 FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND
-    p1.prokind = 'a' AND p2.prokind = 'a' AND
+    p1.proisagg AND p2.proisagg AND
     array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
 ORDER BY 1;
 
@@ -1098,7 +1098,7 @@
 
 SELECT oid, proname
 FROM pg_proc AS p
-WHERE prokind = 'a' AND proargdefaults IS NOT NULL;
+WHERE proisagg AND proargdefaults IS NOT NULL;
 
 -- For the same reason, we avoid creating built-in variadic aggregates, except
 -- that variadic ordered-set aggregates are OK (since they have special syntax
@@ -1106,7 +1106,7 @@
 
 SELECT p.oid, proname
 FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
-WHERE prokind = 'a' AND provariadic != 0 AND a.aggkind = 'n';
+WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
 
 
 -- **************** pg_opfamily ****************

base-commit: b2b82228ee5dc08f0341b5480546479c19e84baf
-- 
2.17.0

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#9)
Re: Odd procedure resolution

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

I think I have made a mistake here. I was reading in between the lines
of a competitor's documentation that they have functions and procedures
in different name spaces, which made me re-read the SQL standard, which
appears to support that approach.

So I'm proposing here a patch to fix that. It is similar to the patch
proposed earlier in the thread, but more extensive.

One open problem in my patch is that regproc/regprocedure don't have a
way to distinguish functions from procedures. Maybe a two-argument
version of to_regprocedure? This will also affect psql's \ef function
and the like.

TBH, this is several months too late for v11. You're talking about a
really fundamental redesign, at least if it's done right and not as a
desperate last-minute hack (which is what this looks like). The points
you make here are just the tip of the iceberg of things that would need
to be reconsidered.

I also remain of the opinion that if we're to separate these namespaces,
the way to do that is to put procedures somewhere other than pg_proc.

Unless you can show that "separate namespaces" is the *only* correct
reading of the SQL spec, which I doubt given the ROUTINE syntax,
I think we're pretty much stuck with the choice we made already.

Or we can push beta back a month or two while we rethink that.
But there's no way you're convincing me that this is a good change
to make four days before beta.

regards, tom lane

#11Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#10)
Re: Odd procedure resolution

On 5/17/18 16:54, Tom Lane wrote:

TBH, this is several months too late for v11.

Maybe, but ...

You're talking about a

really fundamental redesign, at least if it's done right and not as a
desperate last-minute hack (which is what this looks like). The points
you make here are just the tip of the iceberg of things that would need
to be reconsidered.

I disagree with that assessment. The patch is large because it reverts
some earlier changes. But I think the new setup is sound, in large
parts cleaner than before, and addresses various comments that have been
made. I leave it up to the community to decide whether we want to
address this now or later.

One thing to take into consideration is that leaving things as is for
PG11 and then making this change or one like it in PG12 would create
quite a bit of churn in client programs like psql, pg_dump, and probably
things like pgadmin.

I also remain of the opinion that if we're to separate these namespaces,
the way to do that is to put procedures somewhere other than pg_proc.

That would just create an unfathomable amount of code duplication and
mess and unnecessary extra work in PL implementations.

Unless you can show that "separate namespaces" is the *only* correct
reading of the SQL spec, which I doubt given the ROUTINE syntax,
I think we're pretty much stuck with the choice we made already.

I have apparently read the SQL standard differently at different times.
Are you asking me whether I think my current reading is more correct
than my previous ones? Well, yes. But someone else should perhaps
check that. Start with the syntax rules for <SQL-invoked routine>.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#12Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#9)
Re: Odd procedure resolution

On Thu, May 17, 2018 at 4:10 PM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

I think I have made a mistake here. I was reading in between the lines
of a competitor's documentation that they have functions and procedures
in different name spaces, which made me re-read the SQL standard, which
appears to support that approach.

I am really doubtful about trying to merge those completely. You end
up with confusion about what DROP ROUTINE actually means, for example.
Also, I am quite dubious about the idea that functions, window
functions, and aggregates should go all together into one namespace
and procedures into a completely different one. I thought merging all
of that stuff down into prokind was quite elegant, and I'm not too
excited about seeing that change backed out. Functions, procedures,
aggregates, and window functions are all function-like things -- given
any one of them, you might end up writing something like
mything(thingarg1, thingarg2) in some context or other. I think it is
very sensible to say that we won't let you create two such things with
identical signature, because that's just confusing -- and probably of
very doubtful utility. At the same time, I don't think that precludes
using context clues to figure out which one must have been intended in
a particular SQL statement. There are cases where something must
"become all one thing or all the other", but I don't see why that
should be true here.

By the way, if we're going to start worrying about which namespaces
certain competitors put things in, I believe investigation will show
that in at least one notable case, the existing differences are rather
far-reaching and well beyond our ability to fix without major
restructuring of our system catalogs and, I think, abandoning bison.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#13Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Robert Haas (#12)
Re: Odd procedure resolution

On Sat, May 19, 2018 at 12:51 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, May 17, 2018 at 4:10 PM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:

I think I have made a mistake here. I was reading in between the lines
of a competitor's documentation that they have functions and procedures
in different name spaces, which made me re-read the SQL standard, which
appears to support that approach.

I am really doubtful about trying to merge those completely. You end
up with confusion about what DROP ROUTINE actually means, for example.
Also, I am quite dubious about the idea that functions, window
functions, and aggregates should go all together into one namespace
and procedures into a completely different one. I thought merging all
of that stuff down into prokind was quite elegant, and I'm not too
excited about seeing that change backed out. Functions, procedures,
aggregates, and window functions are all function-like things -- given
any one of them, you might end up writing something like
mything(thingarg1, thingarg2) in some context or other. I think it is
very sensible to say that we won't let you create two such things with
identical signature, because that's just confusing -- and probably of
very doubtful utility. At the same time, I don't think that precludes
using context clues to figure out which one must have been intended in
a particular SQL statement. There are cases where something must
"become all one thing or all the other", but I don't see why that
should be true here.

+1 for all that.

--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company