Re: prokind column (was Re: [HACKERS] SQL procedures) - Mailing list pgsql-hackers

From Tom Lane
Subject Re: prokind column (was Re: [HACKERS] SQL procedures)
Date
Msg-id 1636.1519850740@sss.pgh.pa.us
Whole thread Raw
In response to Re: prokind column (was Re: [HACKERS] SQL procedures)  (Michael Paquier <michael@paquier.xyz>)
Responses Re: prokind column (was Re: [HACKERS] SQL procedures)  (Peter Eisentraut <peter.eisentraut@2ndquadrant.com>)
List pgsql-hackers
Michael Paquier <michael@paquier.xyz> writes:
> Here is some input from me. ...

I have reviewed this patch and attach an updated version below.
I've rebased it up to today, fixed a few minor errors, and adopted
most of Michael's suggestions.  Also, since I remain desperately
unhappy with putting zeroes into prorettype, I changed it to not
do that ;-) ... now procedures have VOIDOID as their prorettype,
and it will be substantially less painful to allow them to return
some other scalar result in future, should we wish to.  I believe
I've found all the places that were relying on prorettype == 0 as
a substitute for prokind == 'p'.

I have not touched the tab-complete.c changes.  It doesn't really make
sense to worry about that until we see what comes out of the other thread
discussing support for server-version-sensitive tab completion.  In the
meantime let's just add an open-item entry reminding us to do something
about it before v11 freezes.

Rather than manually audit the 0002 patch, I made a brain-dead little
Perl script to convert the DATA lines automatically, and attach it
below.  If pg_proc.h moves further before this gets committed, the
script could be used to generate an updated 0002 patch.

One point worth noting is that in the attached, I made some edits to
pl_gram.y to preserve the existing handling of RETURN-with-a-value
in a plpgsql procedure.  However, that existing handling is pretty
stupid.  If we simply drop the pl_gram.y diffs below, what happens
is that RETURN-with-a-value is complained of at compile time not
execution time, which seems far superior to me (and more like what
happens in a normal function returning VOID).  I think we should
do that and adjust the regression tests accordingly, but I have not
done the latter here.

I also wonder whether we should drop the stanza at the start of
pg_get_function_result() that causes it to return NULL for a procedure.
Without that, it would now return "void" which I think is at least
as defensible, and certainly more upward-compatible with any future
extension in that area.  (I did make some changes in the
information_schema that cause it to report "void" not NULL in the
same case.)

Other than those points, I think this is committable, and I think we
should get it in quickly.

            regards, tom lane

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2..a0e6d70 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
*************** SCRAM-SHA-256$<replaceable><iteration
*** 5062,5076 ****
    </indexterm>

    <para>
!    The catalog <structname>pg_proc</structname> stores information about functions (or procedures).
!    See <xref linkend="sql-createfunction"/>
!    and <xref linkend="xfunc"/> for more information.
    </para>

    <para>
!    The table contains data for aggregate functions as well as plain functions.
!    If <structfield>proisagg</structfield> is true, there should be a matching
!    row in <structfield>pg_aggregate</structfield>.
    </para>

    <table>
--- 5062,5078 ----
    </indexterm>

    <para>
!    The catalog <structname>pg_proc</structname> stores information about
!    functions, procedures, aggregate functions, and window functions
!    (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
!    aggregate function, there should be a matching row in
!    <structfield>pg_aggregate</structfield>.
    </para>

    <table>
*************** SCRAM-SHA-256$<replaceable><iteration
*** 5157,5173 ****
       </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>
--- 5159,5170 ----
       </row>

       <row>
!       <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>
       </row>

       <row>
*************** SCRAM-SHA-256$<replaceable><iteration
*** 5264,5270 ****
        <entry><structfield>prorettype</structfield></entry>
        <entry><type>oid</type></entry>
        <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
!       <entry>Data type of the return value, or null for a procedure</entry>
       </row>

       <row>
--- 5261,5267 ----
        <entry><structfield>prorettype</structfield></entry>
        <entry><type>oid</type></entry>
        <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
!       <entry>Data type of the return value</entry>
       </row>

       <row>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1156627..3f2c629 100644
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** objectsInSchemaToOids(ObjectType objtype
*** 830,850 ****
                                  BTEqualStrategyNumber, F_OIDEQ,
                                  ObjectIdGetDatum(namespaceId));

-                     /*
-                      * When looking for functions, check for return type <>0.
-                      * When looking for procedures, check for return type ==0.
-                      * When looking for routines, don't check the return type.
-                      */
                      if (objtype == OBJECT_FUNCTION)
                          ScanKeyInit(&key[keycount++],
!                                     Anum_pg_proc_prorettype,
!                                     BTEqualStrategyNumber, F_OIDNE,
!                                     InvalidOid);
                      else if (objtype == OBJECT_PROCEDURE)
                          ScanKeyInit(&key[keycount++],
!                                     Anum_pg_proc_prorettype,
!                                     BTEqualStrategyNumber, F_OIDEQ,
!                                     InvalidOid);

                      rel = heap_open(ProcedureRelationId, AccessShareLock);
                      scan = heap_beginscan_catalog(rel, keycount, key);
--- 830,846 ----
                                  BTEqualStrategyNumber, F_OIDEQ,
                                  ObjectIdGetDatum(namespaceId));

                      if (objtype == OBJECT_FUNCTION)
+                         /* includes aggregates and window functions */
                          ScanKeyInit(&key[keycount++],
!                                     Anum_pg_proc_prokind,
!                                     BTEqualStrategyNumber, F_CHARNE,
!                                     CharGetDatum(PROKIND_PROCEDURE));
                      else if (objtype == OBJECT_PROCEDURE)
                          ScanKeyInit(&key[keycount++],
!                                     Anum_pg_proc_prokind,
!                                     BTEqualStrategyNumber, F_CHAREQ,
!                                     CharGetDatum(PROKIND_PROCEDURE));

                      rel = heap_open(ProcedureRelationId, AccessShareLock);
                      scan = heap_beginscan_catalog(rel, keycount, key);
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 686528c..9a6bf1b 100644
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
*************** CREATE VIEW routines AS
*** 1413,1419 ****
             CAST(current_database() AS sql_identifier) AS routine_catalog,
             CAST(n.nspname AS sql_identifier) AS routine_schema,
             CAST(p.proname AS sql_identifier) AS routine_name,
!            CAST(CASE WHEN p.prorettype <> 0 THEN 'FUNCTION' ELSE 'PROCEDURE' END
               AS character_data) AS routine_type,
             CAST(null AS sql_identifier) AS module_catalog,
             CAST(null AS sql_identifier) AS module_schema,
--- 1413,1419 ----
             CAST(current_database() AS sql_identifier) AS routine_catalog,
             CAST(n.nspname AS sql_identifier) AS routine_schema,
             CAST(p.proname AS sql_identifier) AS routine_name,
!            CAST(CASE p.prokind WHEN 'f' THEN 'FUNCTION' WHEN 'p' THEN 'PROCEDURE' END
               AS character_data) AS routine_type,
             CAST(null AS sql_identifier) AS module_catalog,
             CAST(null AS sql_identifier) AS module_schema,
*************** CREATE VIEW routines AS
*** 1423,1430 ****
             CAST(null AS sql_identifier) AS udt_name,

             CAST(
!              CASE WHEN p.prorettype = 0 THEN NULL
!                   WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ARRAY'
                    WHEN nt.nspname = 'pg_catalog' THEN format_type(t.oid, null)
                    ELSE 'USER-DEFINED' END AS character_data)
               AS data_type,
--- 1423,1429 ----
             CAST(null AS sql_identifier) AS udt_name,

             CAST(
!              CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ARRAY'
                    WHEN nt.nspname = 'pg_catalog' THEN format_type(t.oid, null)
                    ELSE 'USER-DEFINED' END AS character_data)
               AS data_type,
*************** CREATE VIEW routines AS
*** 1442,1448 ****
             CAST(null AS cardinal_number) AS datetime_precision,
             CAST(null AS character_data) AS interval_type,
             CAST(null AS cardinal_number) AS interval_precision,
!            CAST(CASE WHEN p.prorettype <> 0 THEN current_database() END AS sql_identifier) AS type_udt_catalog,
             CAST(nt.nspname AS sql_identifier) AS type_udt_schema,
             CAST(t.typname AS sql_identifier) AS type_udt_name,
             CAST(null AS sql_identifier) AS scope_catalog,
--- 1441,1447 ----
             CAST(null AS cardinal_number) AS datetime_precision,
             CAST(null AS character_data) AS interval_type,
             CAST(null AS cardinal_number) AS interval_precision,
!            CAST(current_database() AS sql_identifier) AS type_udt_catalog,
             CAST(nt.nspname AS sql_identifier) AS type_udt_schema,
             CAST(t.typname AS sql_identifier) AS type_udt_name,
             CAST(null AS sql_identifier) AS scope_catalog,
*************** CREATE VIEW routines AS
*** 1464,1470 ****
             CAST('GENERAL' AS character_data) AS parameter_style,
             CAST(CASE WHEN p.provolatile = 'i' THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_deterministic,
             CAST('MODIFIES' AS character_data) AS sql_data_access,
!            CAST(CASE WHEN p.prorettype <> 0 THEN
               CASE WHEN p.proisstrict THEN 'YES' ELSE 'NO' END END AS yes_or_no) AS is_null_call,
             CAST(null AS character_data) AS sql_path,
             CAST('YES' AS yes_or_no) AS schema_level_routine,
--- 1463,1469 ----
             CAST('GENERAL' AS character_data) AS parameter_style,
             CAST(CASE WHEN p.provolatile = 'i' THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_deterministic,
             CAST('MODIFIES' AS character_data) AS sql_data_access,
!            CAST(CASE WHEN p.prokind <> 'p' THEN
               CASE WHEN p.proisstrict THEN 'YES' ELSE 'NO' END END AS yes_or_no) AS is_null_call,
             CAST(null AS character_data) AS sql_path,
             CAST('YES' AS yes_or_no) AS schema_level_routine,
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 80f561d..119297b 100644
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** getProcedureTypeDescription(StringInfo b
*** 4047,4057 ****
          elog(ERROR, "cache lookup failed for procedure %u", procid);
      procForm = (Form_pg_proc) GETSTRUCT(procTup);

!     if (procForm->proisagg)
          appendStringInfoString(buffer, "aggregate");
!     else if (procForm->prorettype == InvalidOid)
          appendStringInfoString(buffer, "procedure");
!     else
          appendStringInfoString(buffer, "function");

      ReleaseSysCache(procTup);
--- 4047,4057 ----
          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)
          appendStringInfoString(buffer, "procedure");
!     else                        /* function or window function */
          appendStringInfoString(buffer, "function");

      ReleaseSysCache(procTup);
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index f14ea26..50d8d81 100644
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
*************** AggregateCreate(const char *aggName,
*** 616,623 ****
                               InvalidOid,    /* no validator */
                               "aggregate_dummy", /* placeholder proc */
                               NULL,    /* probin */
!                              true,    /* isAgg */
!                              false, /* isWindowFunc */
                               false, /* security invoker (currently not
                                       * definable for agg) */
                               false, /* isLeakProof */
--- 616,622 ----
                               InvalidOid,    /* no validator */
                               "aggregate_dummy", /* placeholder proc */
                               NULL,    /* probin */
!                              PROKIND_AGGREGATE,
                               false, /* security invoker (currently not
                                       * definable for agg) */
                               false, /* isLeakProof */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index b59fadb..f7952b9 100644
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
*************** ProcedureCreate(const char *procedureNam
*** 74,81 ****
                  Oid languageValidator,
                  const char *prosrc,
                  const char *probin,
!                 bool isAgg,
!                 bool isWindowFunc,
                  bool security_definer,
                  bool isLeakProof,
                  bool isStrict,
--- 74,80 ----
                  Oid languageValidator,
                  const char *prosrc,
                  const char *probin,
!                 char prokind,
                  bool security_definer,
                  bool isLeakProof,
                  bool isStrict,
*************** ProcedureCreate(const char *procedureNam
*** 335,342 ****
      values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
      values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
      values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
!     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);
--- 334,340 ----
      values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
      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_prosecdef - 1] = BoolGetDatum(security_definer);
      values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
      values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
*************** ProcedureCreate(const char *procedureNam
*** 403,408 ****
--- 401,421 ----
              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 type"),
+                      (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.
*************** ProcedureCreate(const char *procedureNam
*** 535,568 ****
              }
          }

-         /* 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.
--- 548,553 ----
*************** fmgr_sql_validator(PG_FUNCTION_ARGS)
*** 857,864 ****

      /* Disallow pseudotype result */
      /* except for RECORD, VOID, or polymorphic */
!     if (proc->prorettype &&
!         get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
          proc->prorettype != RECORDOID &&
          proc->prorettype != VOIDOID &&
          !IsPolymorphicType(proc->prorettype))
--- 842,848 ----

      /* Disallow pseudotype result */
      /* except for RECORD, VOID, or polymorphic */
!     if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
          proc->prorettype != RECORDOID &&
          proc->prorettype != VOIDOID &&
          !IsPolymorphicType(proc->prorettype))
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 5652e9e..5e6e8a6 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** WHERE
*** 332,340 ****
  UNION ALL
  SELECT
      l.objoid, l.classoid, l.objsubid,
!     CASE WHEN pro.proisagg = true THEN 'aggregate'::text
!          WHEN pro.proisagg = false THEN 'function'::text
!     END AS objtype,
      pro.pronamespace AS objnamespace,
      CASE WHEN pg_function_is_visible(pro.oid)
           THEN quote_ident(pro.proname)
--- 332,342 ----
  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,
      pro.pronamespace AS objnamespace,
      CASE WHEN pg_function_is_visible(pro.oid)
           THEN quote_ident(pro.proname)
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index fc4ce8d..4b38ef6 100644
*** a/src/backend/commands/dropcmds.c
--- b/src/backend/commands/dropcmds.c
*************** RemoveObjects(DropStmt *stmt)
*** 92,98 ****
           */
          if (stmt->removeType == OBJECT_FUNCTION)
          {
!             if (get_func_isagg(address.objectId))
                  ereport(ERROR,
                          (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                           errmsg("\"%s\" is an aggregate function",
--- 92,98 ----
           */
          if (stmt->removeType == OBJECT_FUNCTION)
          {
!             if (get_func_prokind(address.objectId) == PROKIND_AGGREGATE)
                  ereport(ERROR,
                          (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                           errmsg("\"%s\" is an aggregate function",
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index abdfa24..b1f87d0 100644
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
*************** CreateFunction(ParseState *pstate, Creat
*** 1003,1011 ****

      if (stmt->is_procedure)
      {
          Assert(!stmt->returnType);
!
!         prorettype = InvalidOid;
          returnsSet = false;
      }
      else if (stmt->returnType)
--- 1003,1014 ----

      if (stmt->is_procedure)
      {
+         /*
+          * Sometime in the future, procedures might be allowed to return
+          * results; for now, they all return VOID.
+          */
          Assert(!stmt->returnType);
!         prorettype = VOIDOID;
          returnsSet = false;
      }
      else if (stmt->returnType)
*************** CreateFunction(ParseState *pstate, Creat
*** 1097,1104 ****
                             languageValidator,
                             prosrc_str,    /* converted to text later */
                             probin_str,    /* converted to text later */
!                            false,    /* not an aggregate */
!                            isWindowFunc,
                             security,
                             isLeakProof,
                             isStrict,
--- 1100,1106 ----
                             languageValidator,
                             prosrc_str,    /* converted to text later */
                             probin_str,    /* converted to text later */
!                            stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW :
PROKIND_FUNCTION),
                             security,
                             isLeakProof,
                             isStrict,
*************** RemoveFunctionById(Oid funcOid)
*** 1126,1132 ****
  {
      Relation    relation;
      HeapTuple    tup;
!     bool        isagg;

      /*
       * Delete the pg_proc tuple.
--- 1128,1134 ----
  {
      Relation    relation;
      HeapTuple    tup;
!     char        prokind;

      /*
       * Delete the pg_proc tuple.
*************** RemoveFunctionById(Oid funcOid)
*** 1137,1143 ****
      if (!HeapTupleIsValid(tup)) /* should not happen */
          elog(ERROR, "cache lookup failed for function %u", funcOid);

!     isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;

      CatalogTupleDelete(relation, &tup->t_self);

--- 1139,1145 ----
      if (!HeapTupleIsValid(tup)) /* should not happen */
          elog(ERROR, "cache lookup failed for function %u", funcOid);

!     prokind = ((Form_pg_proc) GETSTRUCT(tup))->prokind;

      CatalogTupleDelete(relation, &tup->t_self);

*************** RemoveFunctionById(Oid funcOid)
*** 1148,1154 ****
      /*
       * If there's a pg_aggregate tuple, delete that too.
       */
!     if (isagg)
      {
          relation = heap_open(AggregateRelationId, RowExclusiveLock);

--- 1150,1156 ----
      /*
       * If there's a pg_aggregate tuple, delete that too.
       */
!     if (prokind == PROKIND_AGGREGATE)
      {
          relation = heap_open(AggregateRelationId, RowExclusiveLock);

*************** AlterFunction(ParseState *pstate, AlterF
*** 1203,1215 ****
          aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
                         NameListToString(stmt->func->objname));

!     if (procForm->proisagg)
          ereport(ERROR,
                  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                   errmsg("\"%s\" is an aggregate function",
                          NameListToString(stmt->func->objname))));

!     is_procedure = (procForm->prorettype == InvalidOid);

      /* Examine requested actions. */
      foreach(l, stmt->actions)
--- 1205,1217 ----
          aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
                         NameListToString(stmt->func->objname));

!     if (procForm->prokind == PROKIND_AGGREGATE)
          ereport(ERROR,
                  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                   errmsg("\"%s\" is an aggregate function",
                          NameListToString(stmt->func->objname))));

!     is_procedure = (procForm->prokind == PROKIND_PROCEDURE);

      /* Examine requested actions. */
      foreach(l, stmt->actions)
*************** CreateCast(CreateCastStmt *stmt)
*** 1525,1538 ****
                      (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                       errmsg("cast function must not be volatile")));
  #endif
!         if (procstruct->proisagg)
!             ereport(ERROR,
!                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
!                      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),
--- 1527,1536 ----
                      (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                       errmsg("cast function must not be volatile")));
  #endif
!         if (procstruct->prokind != PROKIND_FUNCTION)
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
!                      errmsg("cast function must be a normal function")));
          if (procstruct->proretset)
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
*************** check_transform_function(Form_pg_proc pr
*** 1777,1790 ****
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                   errmsg("transform function must not be volatile")));
!     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 not be a window function")));
      if (procstruct->proretset)
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
--- 1775,1784 ----
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                   errmsg("transform function must not be volatile")));
!     if (procstruct->prokind != PROKIND_FUNCTION)
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
!                  errmsg("transform function must be a normal function")));
      if (procstruct->proretset)
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 2ec9624..447bd49 100644
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
*************** CreateProceduralLanguage(CreatePLangStmt
*** 129,136 ****
                                        F_FMGR_C_VALIDATOR,
                                        pltemplate->tmplhandler,
                                        pltemplate->tmpllibrary,
!                                       false,    /* isAgg */
!                                       false,    /* isWindowFunc */
                                        false,    /* security_definer */
                                        false,    /* isLeakProof */
                                        false,    /* isStrict */
--- 129,135 ----
                                        F_FMGR_C_VALIDATOR,
                                        pltemplate->tmplhandler,
                                        pltemplate->tmpllibrary,
!                                       PROKIND_FUNCTION,
                                        false,    /* security_definer */
                                        false,    /* isLeakProof */
                                        false,    /* isStrict */
*************** CreateProceduralLanguage(CreatePLangStmt
*** 169,176 ****
                                            F_FMGR_C_VALIDATOR,
                                            pltemplate->tmplinline,
                                            pltemplate->tmpllibrary,
!                                           false,    /* isAgg */
!                                           false,    /* isWindowFunc */
                                            false,    /* security_definer */
                                            false,    /* isLeakProof */
                                            true, /* isStrict */
--- 168,174 ----
                                            F_FMGR_C_VALIDATOR,
                                            pltemplate->tmplinline,
                                            pltemplate->tmpllibrary,
!                                           PROKIND_FUNCTION,
                                            false,    /* security_definer */
                                            false,    /* isLeakProof */
                                            true, /* isStrict */
*************** CreateProceduralLanguage(CreatePLangStmt
*** 212,219 ****
                                            F_FMGR_C_VALIDATOR,
                                            pltemplate->tmplvalidator,
                                            pltemplate->tmpllibrary,
!                                           false,    /* isAgg */
!                                           false,    /* isWindowFunc */
                                            false,    /* security_definer */
                                            false,    /* isLeakProof */
                                            true, /* isStrict */
--- 210,216 ----
                                            F_FMGR_C_VALIDATOR,
                                            pltemplate->tmplvalidator,
                                            pltemplate->tmpllibrary,
!                                           PROKIND_FUNCTION,
                                            false,    /* security_definer */
                                            false,    /* isLeakProof */
                                            true, /* isStrict */
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 899a5c4..bf3cd3a 100644
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** makeRangeConstructors(const char *name,
*** 1672,1679 ****
                                   F_FMGR_INTERNAL_VALIDATOR, /* language validator */
                                   prosrc[i], /* prosrc */
                                   NULL,    /* probin */
!                                  false, /* isAgg */
!                                  false, /* isWindowFunc */
                                   false, /* security_definer */
                                   false, /* leakproof */
                                   false, /* isStrict */
--- 1672,1678 ----
                                   F_FMGR_INTERNAL_VALIDATOR, /* language validator */
                                   prosrc[i], /* prosrc */
                                   NULL,    /* probin */
!                                  PROKIND_FUNCTION,
                                   false, /* security_definer */
                                   false, /* leakproof */
                                   false, /* isStrict */
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 7e249f5..78bc4ab 100644
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
*************** init_execution_state(List *queryTree_lis
*** 573,580 ****
       *
       * Note: don't set setsResult if the function returns VOID, as evidenced
       * by not having made a junkfilter.  This ensures we'll throw away any
!      * output from a utility statement that check_sql_fn_retval deemed to not
!      * have output.
       */
      if (lasttages && fcache->junkFilter)
      {
--- 573,579 ----
       *
       * Note: don't set setsResult if the function returns VOID, as evidenced
       * by not having made a junkfilter.  This ensures we'll throw away any
!      * output from the last statement in such a function.
       */
      if (lasttages && fcache->junkFilter)
      {
*************** init_sql_fcache(FmgrInfo *finfo, Oid col
*** 659,666 ****
      fcache->rettype = rettype;

      /* Fetch the typlen and byval info for the result type */
!     if (rettype)
!         get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);

      /* Remember whether we're returning setof something */
      fcache->returnsSet = procedureStruct->proretset;
--- 658,664 ----
      fcache->rettype = rettype;

      /* Fetch the typlen and byval info for the result type */
!     get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);

      /* Remember whether we're returning setof something */
      fcache->returnsSet = procedureStruct->proretset;
*************** fmgr_sql(PG_FUNCTION_ARGS)
*** 1324,1331 ****
          }
          else
          {
!             /* Should only get here for procedures and VOID functions */
!             Assert(fcache->rettype == InvalidOid || fcache->rettype == VOIDOID);
              fcinfo->isnull = true;
              result = (Datum) 0;
          }
--- 1322,1329 ----
          }
          else
          {
!             /* Should only get here for VOID functions and procedures */
!             Assert(fcache->rettype == VOIDOID);
              fcinfo->isnull = true;
              result = (Datum) 0;
          }
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1549,1557 ****
      if (modifyTargetList)
          *modifyTargetList = false;    /* initialize for no change */
      if (junkFilter)
!         *junkFilter = NULL;        /* initialize in case of procedure/VOID result */

!     if (!rettype)
          return false;

      /*
--- 1547,1559 ----
      if (modifyTargetList)
          *modifyTargetList = false;    /* initialize for no change */
      if (junkFilter)
!         *junkFilter = NULL;        /* initialize in case of VOID result */

!     /*
!      * If it's declared to return VOID, we don't care what's in the function.
!      * (This takes care of the procedure case, as well.)
!      */
!     if (rettype == VOIDOID)
          return false;

      /*
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1597,1617 ****
      else
      {
          /* Empty function body, or last statement is a utility command */
!         if (rettype && rettype != VOIDOID)
!             ereport(ERROR,
!                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
!                      errmsg("return type mismatch in function declared to return %s",
!                             format_type_be(rettype)),
!                      errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE RETURNING.")));
!         return false;
      }

      /*
       * OK, check that the targetlist returns something matching the declared
!      * type.  (We used to insist that the declared type not be VOID in this
!      * case, but that makes it hard to write a void function that exits after
!      * calling another void function.  Instead, we insist that the tlist
!      * return void ... so void is treated as if it were a scalar type below.)
       */

      /*
--- 1599,1615 ----
      else
      {
          /* Empty function body, or last statement is a utility command */
!         ereport(ERROR,
!                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
!                  errmsg("return type mismatch in function declared to return %s",
!                         format_type_be(rettype)),
!                  errdetail("Function's final statement must be SELECT or INSERT/UPDATE/DELETE RETURNING.")));
!         return false;            /* keep compiler quiet */
      }

      /*
       * OK, check that the targetlist returns something matching the declared
!      * type.
       */

      /*
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 1624,1631 ****
      if (fn_typtype == TYPTYPE_BASE ||
          fn_typtype == TYPTYPE_DOMAIN ||
          fn_typtype == TYPTYPE_ENUM ||
!         fn_typtype == TYPTYPE_RANGE ||
!         rettype == VOIDOID)
      {
          /*
           * For scalar-type returns, the target list must have exactly one
--- 1622,1628 ----
      if (fn_typtype == TYPTYPE_BASE ||
          fn_typtype == TYPTYPE_DOMAIN ||
          fn_typtype == TYPTYPE_ENUM ||
!         fn_typtype == TYPTYPE_RANGE)
      {
          /*
           * For scalar-type returns, the target list must have exactly one
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 89f27ce..a9a09af 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** inline_function(Oid funcid, Oid result_t
*** 4484,4495 ****

      /*
       * Forget it if the function is not SQL-language or has other showstopper
!      * properties.  (The nargs check is just paranoia.)
       */
      if (funcform->prolang != SQLlanguageId ||
          funcform->prosecdef ||
          funcform->proretset ||
-         funcform->prorettype == InvalidOid ||
          funcform->prorettype == RECORDOID ||
          !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
          funcform->pronargs != list_length(args))
--- 4484,4495 ----

      /*
       * Forget it if the function is not SQL-language or has other showstopper
!      * properties.  (The prokind and nargs checks are just paranoia.)
       */
      if (funcform->prolang != SQLlanguageId ||
          funcform->prosecdef ||
+         funcform->prokind != PROKIND_FUNCTION ||
          funcform->proretset ||
          funcform->prorettype == RECORDOID ||
          !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
          funcform->pronargs != list_length(args))
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 085aa87..665d332 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
*************** build_coercion_expression(Node *node,
*** 834,841 ****
           */
          /* Assert(targetTypeId == procstruct->prorettype); */
          Assert(!procstruct->proretset);
!         Assert(!procstruct->proisagg);
!         Assert(!procstruct->proiswindow);
          nargs = procstruct->pronargs;
          Assert(nargs >= 1 && nargs <= 3);
          /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
--- 834,840 ----
           */
          /* Assert(targetTypeId == procstruct->prorettype); */
          Assert(!procstruct->proretset);
!         Assert(procstruct->prokind == PROKIND_FUNCTION);
          nargs = procstruct->pronargs;
          Assert(nargs >= 1 && nargs <= 3);
          /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 2a4ac09..ea5d521 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
*************** func_get_detail(List *funcname,
*** 1614,1627 ****
                  *argdefaults = defaults;
              }
          }
!         if (pform->proisagg)
!             result = FUNCDETAIL_AGGREGATE;
!         else if (pform->proiswindow)
!             result = FUNCDETAIL_WINDOWFUNC;
!         else if (pform->prorettype == InvalidOid)
!             result = FUNCDETAIL_PROCEDURE;
!         else
!             result = FUNCDETAIL_NORMAL;
          ReleaseSysCache(ftup);
          return result;
      }
--- 1614,1640 ----
                  *argdefaults = defaults;
              }
          }
!
!         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;
!         }
!
          ReleaseSysCache(ftup);
          return result;
      }
*************** LookupFuncWithArgs(ObjectType objtype, O
*** 2067,2073 ****
      if (objtype == OBJECT_FUNCTION)
      {
          /* Make sure it's a function, not a procedure */
!         if (oid && get_func_rettype(oid) == InvalidOid)
          {
              if (noError)
                  return InvalidOid;
--- 2080,2086 ----
      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;
*************** LookupFuncWithArgs(ObjectType objtype, O
*** 2098,2104 ****
          }

          /* Make sure it's a procedure */
!         if (get_func_rettype(oid) != InvalidOid)
          {
              if (noError)
                  return InvalidOid;
--- 2111,2117 ----
          }

          /* Make sure it's a procedure */
!         if (get_func_prokind(oid) != PROKIND_PROCEDURE)
          {
              if (noError)
                  return InvalidOid;
*************** LookupFuncWithArgs(ObjectType objtype, O
*** 2134,2140 ****
          }

          /* Make sure it's an aggregate */
!         if (!get_func_isagg(oid))
          {
              if (noError)
                  return InvalidOid;
--- 2147,2153 ----
          }

          /* Make sure it's an aggregate */
!         if (get_func_prokind(oid) != PROKIND_AGGREGATE)
          {
              if (noError)
                  return InvalidOid;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3697466..b58ee3c 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** pg_get_functiondef(PG_FUNCTION_ARGS)
*** 2481,2492 ****
      proc = (Form_pg_proc) GETSTRUCT(proctup);
      name = NameStr(proc->proname);

!     if (proc->proisagg)
          ereport(ERROR,
                  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                   errmsg("\"%s\" is an aggregate function", name)));

!     isfunction = (proc->prorettype != InvalidOid);

      /*
       * We always qualify the function name, to ensure the right function gets
--- 2481,2492 ----
      proc = (Form_pg_proc) GETSTRUCT(proctup);
      name = NameStr(proc->proname);

!     if (proc->prokind == PROKIND_AGGREGATE)
          ereport(ERROR,
                  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                   errmsg("\"%s\" is an aggregate function", name)));

!     isfunction = (proc->prokind != PROKIND_PROCEDURE);

      /*
       * We always qualify the function name, to ensure the right function gets
*************** pg_get_functiondef(PG_FUNCTION_ARGS)
*** 2513,2519 ****
      /* Emit some miscellaneous options on one line */
      oldlen = buf.len;

!     if (proc->proiswindow)
          appendStringInfoString(&buf, " WINDOW");
      switch (proc->provolatile)
      {
--- 2513,2519 ----
      /* Emit some miscellaneous options on one line */
      oldlen = buf.len;

!     if (proc->prokind == PROKIND_WINDOW)
          appendStringInfoString(&buf, " WINDOW");
      switch (proc->provolatile)
      {
*************** pg_get_function_result(PG_FUNCTION_ARGS)
*** 2717,2723 ****
      if (!HeapTupleIsValid(proctup))
          PG_RETURN_NULL();

!     if (((Form_pg_proc) GETSTRUCT(proctup))->prorettype == InvalidOid)
      {
          ReleaseSysCache(proctup);
          PG_RETURN_NULL();
--- 2717,2723 ----
      if (!HeapTupleIsValid(proctup))
          PG_RETURN_NULL();

!     if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
      {
          ReleaseSysCache(proctup);
          PG_RETURN_NULL();
*************** print_function_arguments(StringInfo buf,
*** 2817,2823 ****
      }

      /* Check for special treatment of ordered-set aggregates */
!     if (proc->proisagg)
      {
          HeapTuple    aggtup;
          Form_pg_aggregate agg;
--- 2817,2823 ----
      }

      /* Check for special treatment of ordered-set aggregates */
!     if (proc->prokind == PROKIND_AGGREGATE)
      {
          HeapTuple    aggtup;
          Form_pg_aggregate agg;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 51b6b4f..bba595a 100644
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
*************** func_parallel(Oid funcid)
*** 1600,1619 ****
  }

  /*
!  * get_func_isagg
!  *       Given procedure id, return the function's proisagg field.
   */
! bool
! get_func_isagg(Oid funcid)
  {
      HeapTuple    tp;
!     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))->proisagg;
      ReleaseSysCache(tp);
      return result;
  }
--- 1600,1619 ----
  }

  /*
!  * get_func_prokind
!  *       Given procedure id, return the routine kind.
   */
! char
! get_func_prokind(Oid funcid)
  {
      HeapTuple    tp;
!     char        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;
      ReleaseSysCache(tp);
      return result;
  }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2c934e6..566cbf2 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** getAggregates(Archive *fout, int *numAgg
*** 5407,5417 ****
--- 5407,5421 ----
          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, "
*************** getAggregates(Archive *fout, int *numAgg
*** 5426,5432 ****
                            "(p.oid = pip.objoid "
                            "AND pip.classoid = 'pg_proc'::regclass "
                            "AND pip.objsubid = 0) "
!                           "WHERE p.proisagg AND ("
                            "p.pronamespace != "
                            "(SELECT oid FROM pg_namespace "
                            "WHERE nspname = 'pg_catalog') OR "
--- 5430,5436 ----
                            "(p.oid = pip.objoid "
                            "AND pip.classoid = 'pg_proc'::regclass "
                            "AND pip.objsubid = 0) "
!                           "WHERE %s AND ("
                            "p.pronamespace != "
                            "(SELECT oid FROM pg_namespace "
                            "WHERE nspname = 'pg_catalog') OR "
*************** getAggregates(Archive *fout, int *numAgg
*** 5435,5441 ****
                            acl_subquery->data,
                            racl_subquery->data,
                            initacl_subquery->data,
!                           initracl_subquery->data);
          if (dopt->binary_upgrade)
              appendPQExpBufferStr(query,
                                   " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
--- 5439,5446 ----
                            acl_subquery->data,
                            racl_subquery->data,
                            initacl_subquery->data,
!                           initracl_subquery->data,
!                           agg_check);
          if (dopt->binary_upgrade)
              appendPQExpBufferStr(query,
                                   " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
*************** getFuncs(Archive *fout, int *numFuncs)
*** 5616,5626 ****
--- 5621,5635 ----
          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, "
*************** getFuncs(Archive *fout, int *numFuncs)
*** 5635,5641 ****
                            "(p.oid = pip.objoid "
                            "AND pip.classoid = 'pg_proc'::regclass "
                            "AND pip.objsubid = 0) "
!                           "WHERE NOT proisagg"
                            "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
                            "WHERE classid = 'pg_proc'::regclass AND "
                            "objid = p.oid AND deptype = 'i')"
--- 5644,5650 ----
                            "(p.oid = pip.objoid "
                            "AND pip.classoid = 'pg_proc'::regclass "
                            "AND pip.objsubid = 0) "
!                           "WHERE %s"
                            "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
                            "WHERE classid = 'pg_proc'::regclass AND "
                            "objid = p.oid AND deptype = 'i')"
*************** getFuncs(Archive *fout, int *numFuncs)
*** 5655,5660 ****
--- 5664,5670 ----
                            initacl_subquery->data,
                            initracl_subquery->data,
                            username_subquery,
+                           not_agg_check,
                            g_last_builtin_oid,
                            g_last_builtin_oid);
          if (dopt->binary_upgrade)
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11424,11435 ****
      char       *funcargs;
      char       *funciargs;
      char       *funcresult;
-     bool        is_procedure;
      char       *proallargtypes;
      char       *proargmodes;
      char       *proargnames;
      char       *protrftypes;
!     char       *proiswindow;
      char       *provolatile;
      char       *proisstrict;
      char       *prosecdef;
--- 11434,11444 ----
      char       *funcargs;
      char       *funciargs;
      char       *funcresult;
      char       *proallargtypes;
      char       *proargmodes;
      char       *proargnames;
      char       *protrftypes;
!     char       *prokind;
      char       *provolatile;
      char       *proisstrict;
      char       *prosecdef;
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11459,11465 ****
      asPart = createPQExpBuffer();

      /* Fetch function-specific details */
!     if (fout->remoteVersion >= 90600)
      {
          /*
           * proparallel was added in 9.6
--- 11468,11493 ----
      asPart = createPQExpBuffer();

      /* Fetch function-specific details */
!     if (fout->remoteVersion >= 110000)
!     {
!         /*
!          * prokind was added in 11
!          */
!         appendPQExpBuffer(query,
!                           "SELECT proretset, prosrc, probin, "
!                           "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, "
!                           "array_to_string(protrftypes, ' ') AS protrftypes, "
!                           "prokind, provolatile, proisstrict, prosecdef, "
!                           "proleakproof, proconfig, procost, prorows, "
!                           "proparallel, "
!                           "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
!                           "FROM pg_catalog.pg_proc "
!                           "WHERE oid = '%u'::pg_catalog.oid",
!                           finfo->dobj.catId.oid);
!     }
!     else if (fout->remoteVersion >= 90600)
      {
          /*
           * proparallel was added in 9.6
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11470,11476 ****
                            "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, "
!                           "proiswindow, provolatile, proisstrict, prosecdef, "
                            "proleakproof, proconfig, procost, prorows, "
                            "proparallel, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
--- 11498,11505 ----
                            "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, "
                            "proleakproof, proconfig, procost, prorows, "
                            "proparallel, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11489,11495 ****
                            "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, "
!                           "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 "
--- 11518,11525 ----
                            "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, "
                            "proleakproof, proconfig, procost, prorows, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                            "FROM pg_catalog.pg_proc "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11506,11512 ****
                            "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, "
!                           "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 "
--- 11536,11543 ----
                            "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, "
                            "proleakproof, proconfig, procost, prorows, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
                            "FROM pg_catalog.pg_proc "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11524,11530 ****
                            "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, "
!                           "proiswindow, provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            " proconfig, procost, prorows, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
--- 11555,11562 ----
                            "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, "
                            "false AS proleakproof, "
                            " proconfig, procost, prorows, "
                            "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11537,11543 ****
          appendPQExpBuffer(query,
                            "SELECT proretset, prosrc, probin, "
                            "proallargtypes, proargmodes, proargnames, "
!                           "false AS proiswindow, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "proconfig, procost, prorows, "
--- 11569,11575 ----
          appendPQExpBuffer(query,
                            "SELECT proretset, prosrc, probin, "
                            "proallargtypes, proargmodes, proargnames, "
!                           "'f' AS prokind, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "proconfig, procost, prorows, "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11551,11557 ****
          appendPQExpBuffer(query,
                            "SELECT proretset, prosrc, probin, "
                            "proallargtypes, proargmodes, proargnames, "
!                           "false AS proiswindow, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "null AS proconfig, 0 AS procost, 0 AS prorows, "
--- 11583,11589 ----
          appendPQExpBuffer(query,
                            "SELECT proretset, prosrc, probin, "
                            "proallargtypes, proargmodes, proargnames, "
!                           "'f' AS prokind, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "null AS proconfig, 0 AS procost, 0 AS prorows, "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11567,11573 ****
                            "null AS proallargtypes, "
                            "null AS proargmodes, "
                            "proargnames, "
!                           "false AS proiswindow, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "null AS proconfig, 0 AS procost, 0 AS prorows, "
--- 11599,11605 ----
                            "null AS proallargtypes, "
                            "null AS proargmodes, "
                            "proargnames, "
!                           "'f' AS prokind, "
                            "provolatile, proisstrict, prosecdef, "
                            "false AS proleakproof, "
                            "null AS proconfig, 0 AS procost, 0 AS prorows, "
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11586,11596 ****
      {
          funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
          funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
!         is_procedure = PQgetisnull(res, 0, PQfnumber(res, "funcresult"));
!         if (is_procedure)
!             funcresult = NULL;
!         else
!             funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
          proallargtypes = proargmodes = proargnames = NULL;
      }
      else
--- 11618,11624 ----
      {
          funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
          funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
!         funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
          proallargtypes = proargmodes = proargnames = NULL;
      }
      else
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11599,11611 ****
          proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
          proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
          funcargs = funciargs = funcresult = NULL;
-         is_procedure = false;
      }
      if (PQfnumber(res, "protrftypes") != -1)
          protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
      else
          protrftypes = NULL;
!     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"));
--- 11627,11638 ----
          proargmodes = PQgetvalue(res, 0, PQfnumber(res, "proargmodes"));
          proargnames = PQgetvalue(res, 0, PQfnumber(res, "proargnames"));
          funcargs = funciargs = funcresult = NULL;
      }
      if (PQfnumber(res, "protrftypes") != -1)
          protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
      else
          protrftypes = NULL;
!     prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
      provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
      proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
      prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11731,11737 ****

      funcsig_tag = format_function_signature(fout, finfo, false);

!     keyword = is_procedure ? "PROCEDURE" : "FUNCTION";

      appendPQExpBuffer(delqry, "DROP %s %s.%s;\n",
                        keyword,
--- 11758,11767 ----

      funcsig_tag = format_function_signature(fout, finfo, false);

!     if (prokind[0] == PROKIND_PROCEDURE)
!         keyword = "PROCEDURE";
!     else
!         keyword = "FUNCTION";    /* works for window functions too */

      appendPQExpBuffer(delqry, "DROP %s %s.%s;\n",
                        keyword,
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11744,11751 ****
                        funcfullsig ? funcfullsig :
                        funcsig);

!     if (is_procedure)
!         ;
      else if (funcresult)
          appendPQExpBuffer(q, " RETURNS %s", funcresult);
      else
--- 11774,11781 ----
                        funcfullsig ? funcfullsig :
                        funcsig);

!     if (prokind[0] == PROKIND_PROCEDURE)
!          /* no result type to output */ ;
      else if (funcresult)
          appendPQExpBuffer(q, " RETURNS %s", funcresult);
      else
*************** dumpFunc(Archive *fout, FuncInfo *finfo)
*** 11776,11782 ****
          }
      }

!     if (proiswindow[0] == 't')
          appendPQExpBufferStr(q, " WINDOW");

      if (provolatile[0] != PROVOLATILE_VOLATILE)
--- 11806,11812 ----
          }
      }

!     if (prokind[0] == PROKIND_WINDOW)
          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 6f74e15..1bea6ae 100644
*** a/src/bin/pg_dump/t/002_pg_dump.pl
--- b/src/bin/pg_dump/t/002_pg_dump.pl
*************** qr/^GRANT SELECT ON TABLE dump_test_seco
*** 6257,6264 ****
                             prorows,
                             provariadic,
                             protransform,
!                            proisagg,
!                            proiswindow,
                             prosecdef,
                             proleakproof,
                             proisstrict,
--- 6257,6263 ----
                             prorows,
                             provariadic,
                             protransform,
!                            prokind,
                             prosecdef,
                             proleakproof,
                             proisstrict,
*************** qr/^GRANT SELECT ON TABLE dump_test_seco
*** 6290,6297 ****
          \QGRANT SELECT(prorows) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
          \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(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.*
--- 6289,6295 ----
          \QGRANT SELECT(prorows) ON TABLE pg_catalog.pg_proc TO PUBLIC;\E\n.*
          \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(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 466a780..0c3be1f 100644
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** describeAggregates(const char *pattern,
*** 100,111 ****
                            "  pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
                            gettext_noop("Argument data types"));

!     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"
--- 100,119 ----
                            "  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"));

      if (!showSystem && !pattern)
          appendPQExpBufferStr(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** describeFunctions(const char *functypes,
*** 346,359 ****
                        gettext_noop("Schema"),
                        gettext_noop("Name"));

!     if (pset.sversion >= 80400)
          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\n"
                            "  WHEN p.proisagg THEN '%s'\n"
                            "  WHEN p.proiswindow THEN '%s'\n"
-                           "  WHEN p.prorettype = 0 THEN '%s'\n"
                            "  WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
                            "  ELSE '%s'\n"
                            " END as \"%s\"",
--- 354,384 ----
                        gettext_noop("Schema"),
                        gettext_noop("Name"));

!     if (pset.sversion >= 110000)
!         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"
!                           "  ELSE '%s'\n"
!                           " END as \"%s\"",
!                           gettext_noop("Result data type"),
!                           gettext_noop("Argument data types"),
!         /* 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)
          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\n"
                            "  WHEN p.proisagg THEN '%s'\n"
                            "  WHEN p.proiswindow THEN '%s'\n"
                            "  WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
                            "  ELSE '%s'\n"
                            " END as \"%s\"",
*************** describeFunctions(const char *functypes,
*** 362,368 ****
          /* translator: "agg" is short for "aggregate" */
                            gettext_noop("agg"),
                            gettext_noop("window"),
-                           gettext_noop("proc"),
                            gettext_noop("trigger"),
                            gettext_noop("func"),
                            gettext_noop("Type"));
--- 387,392 ----
*************** describeFunctions(const char *functypes,
*** 494,500 ****
                  appendPQExpBufferStr(&buf, "WHERE ");
                  have_where = true;
              }
!             appendPQExpBufferStr(&buf, "NOT p.proisagg\n");
          }
          if (!showTrigger)
          {
--- 518,527 ----
                  appendPQExpBufferStr(&buf, "WHERE ");
                  have_where = true;
              }
!             if (pset.sversion >= 110000)
!                 appendPQExpBufferStr(&buf, "p.prokind <> 'a'\n");
!             else
!                 appendPQExpBufferStr(&buf, "NOT p.proisagg\n");
          }
          if (!showTrigger)
          {
*************** describeFunctions(const char *functypes,
*** 516,522 ****
                  appendPQExpBufferStr(&buf, "WHERE ");
                  have_where = true;
              }
!             appendPQExpBufferStr(&buf, "NOT p.proiswindow\n");
          }
      }
      else
--- 543,552 ----
                  appendPQExpBufferStr(&buf, "WHERE ");
                  have_where = true;
              }
!             if (pset.sversion >= 110000)
!                 appendPQExpBufferStr(&buf, "p.prokind <> 'w'\n");
!             else
!                 appendPQExpBufferStr(&buf, "NOT p.proiswindow\n");
          }
      }
      else
*************** describeFunctions(const char *functypes,
*** 528,534 ****
          /* Note: at least one of these must be true ... */
          if (showAggregate)
          {
!             appendPQExpBufferStr(&buf, "p.proisagg\n");
              needs_or = true;
          }
          if (showTrigger)
--- 558,567 ----
          /* Note: at least one of these must be true ... */
          if (showAggregate)
          {
!             if (pset.sversion >= 110000)
!                 appendPQExpBufferStr(&buf, "p.prokind = 'a'\n");
!             else
!                 appendPQExpBufferStr(&buf, "p.proisagg\n");
              needs_or = true;
          }
          if (showTrigger)
*************** describeFunctions(const char *functypes,
*** 543,549 ****
          {
              if (needs_or)
                  appendPQExpBufferStr(&buf, "       OR ");
!             appendPQExpBufferStr(&buf, "p.proiswindow\n");
              needs_or = true;
          }
          appendPQExpBufferStr(&buf, "      )\n");
--- 576,585 ----
          {
              if (needs_or)
                  appendPQExpBufferStr(&buf, "       OR ");
!             if (pset.sversion >= 110000)
!                 appendPQExpBufferStr(&buf, "p.prokind = 'w'\n");
!             else
!                 appendPQExpBufferStr(&buf, "p.proiswindow\n");
              needs_or = true;
          }
          appendPQExpBufferStr(&buf, "      )\n");
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8bc4a19..cf23d13 100644
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** static const SchemaQuery Query_for_list_
*** 349,355 ****
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.proisagg",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
--- 349,355 ----
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.prokind = 'a'",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
*************** static const SchemaQuery Query_for_list_
*** 397,403 ****
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.prorettype <> 0",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
--- 397,403 ----
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.prokind IN ('f', 'w')",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
*************** static const SchemaQuery Query_for_list_
*** 428,434 ****
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.prorettype = 0",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
--- 428,434 ----
      /* catname */
      "pg_catalog.pg_proc p",
      /* selcondition */
!     "p.prokind = 'p'",
      /* viscondition */
      "pg_catalog.pg_function_is_visible(p.oid)",
      /* namespace */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 26b1866..01cf59e 100644
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
*************** DATA(insert OID = 1247 (  pg_type        PGNSP
*** 151,157 ****
  DESCR("");
  DATA(insert OID = 1249 (  pg_attribute    PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc        PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
  DATA(insert OID = 1259 (  pg_class        PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 33 0 t f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
--- 151,157 ----
  DESCR("");
  DATA(insert OID = 1249 (  pg_attribute    PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 22 0 f f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
! DATA(insert OID = 1255 (  pg_proc        PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
  DATA(insert OID = 1259 (  pg_class        PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 33 0 t f f f f f f t n f 3 1 _null_
_null__null_)); 
  DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c00d055..b25c918 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_
*** 43,50 ****
      float4        prorows;        /* estimated # of rows out (if proretset) */
      Oid            provariadic;    /* element type of variadic array, or 0 */
      regproc        protransform;    /* transforms calls to it during planning */
!     bool        proisagg;        /* is it an aggregate? */
!     bool        proiswindow;    /* is it a window function? */
      bool        prosecdef;        /* security definer */
      bool        proleakproof;    /* is it a leak-proof function? */
      bool        proisstrict;    /* strict with respect to NULLs? */
--- 43,49 ----
      float4        prorows;        /* estimated # of rows out (if proretset) */
      Oid            provariadic;    /* element type of variadic array, or 0 */
      regproc        protransform;    /* transforms calls to it during planning */
!     char        prokind;        /* see PROKIND_ categories below */
      bool        prosecdef;        /* security definer */
      bool        proleakproof;    /* is it a leak-proof function? */
      bool        proisstrict;    /* strict with respect to NULLs? */
*************** typedef FormData_pg_proc *Form_pg_proc;
*** 86,92 ****
   *        compiler constants for pg_proc
   * ----------------
   */
! #define Natts_pg_proc                    29
  #define Anum_pg_proc_proname            1
  #define Anum_pg_proc_pronamespace        2
  #define Anum_pg_proc_proowner            3
--- 85,91 ----
   *        compiler constants for pg_proc
   * ----------------
   */
! #define Natts_pg_proc                    28
  #define Anum_pg_proc_proname            1
  #define Anum_pg_proc_pronamespace        2
  #define Anum_pg_proc_proowner            3
*************** typedef FormData_pg_proc *Form_pg_proc;
*** 95,121 ****
  #define Anum_pg_proc_prorows            6
  #define Anum_pg_proc_provariadic        7
  #define Anum_pg_proc_protransform        8
! #define Anum_pg_proc_proisagg            9
! #define Anum_pg_proc_proiswindow        10
! #define Anum_pg_proc_prosecdef            11
! #define Anum_pg_proc_proleakproof        12
! #define Anum_pg_proc_proisstrict        13
! #define Anum_pg_proc_proretset            14
! #define Anum_pg_proc_provolatile        15
! #define Anum_pg_proc_proparallel        16
! #define Anum_pg_proc_pronargs            17
! #define Anum_pg_proc_pronargdefaults    18
! #define Anum_pg_proc_prorettype            19
! #define Anum_pg_proc_proargtypes        20
! #define Anum_pg_proc_proallargtypes        21
! #define Anum_pg_proc_proargmodes        22
! #define Anum_pg_proc_proargnames        23
! #define Anum_pg_proc_proargdefaults        24
! #define Anum_pg_proc_protrftypes        25
! #define Anum_pg_proc_prosrc                26
! #define Anum_pg_proc_probin                27
! #define Anum_pg_proc_proconfig            28
! #define Anum_pg_proc_proacl                29

  /* ----------------
   *        initial contents of pg_proc
--- 94,119 ----
  #define Anum_pg_proc_prorows            6
  #define Anum_pg_proc_provariadic        7
  #define Anum_pg_proc_protransform        8
! #define Anum_pg_proc_prokind            9
! #define Anum_pg_proc_prosecdef            10
! #define Anum_pg_proc_proleakproof        11
! #define Anum_pg_proc_proisstrict        12
! #define Anum_pg_proc_proretset            13
! #define Anum_pg_proc_provolatile        14
! #define Anum_pg_proc_proparallel        15
! #define Anum_pg_proc_pronargs            16
! #define Anum_pg_proc_pronargdefaults    17
! #define Anum_pg_proc_prorettype            18
! #define Anum_pg_proc_proargtypes        19
! #define Anum_pg_proc_proallargtypes        20
! #define Anum_pg_proc_proargmodes        21
! #define Anum_pg_proc_proargnames        22
! #define Anum_pg_proc_proargdefaults        23
! #define Anum_pg_proc_protrftypes        24
! #define Anum_pg_proc_prosrc                25
! #define Anum_pg_proc_probin                26
! #define Anum_pg_proc_proconfig            27
! #define Anum_pg_proc_proacl                28

  /* ----------------
   *        initial contents of pg_proc
*************** DATA(insert OID = 5028 ( satisfies_hash_
*** 5576,5581 ****
--- 5574,5587 ----
  DESCR("hash partition CHECK constraint");

  /*
+  * Symbolic values for prokind column
+  */
+ #define PROKIND_FUNCTION 'f'
+ #define PROKIND_AGGREGATE 'a'
+ #define PROKIND_WINDOW 'w'
+ #define PROKIND_PROCEDURE 'p'
+
+ /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
   * or can change due to outside factors (such as parameter variables or
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
index 098e2e6..b66871b 100644
*** a/src/include/catalog/pg_proc_fn.h
--- b/src/include/catalog/pg_proc_fn.h
*************** extern ObjectAddress ProcedureCreate(con
*** 27,34 ****
                  Oid languageValidator,
                  const char *prosrc,
                  const char *probin,
!                 bool isAgg,
!                 bool isWindowFunc,
                  bool security_definer,
                  bool isLeakProof,
                  bool isStrict,
--- 27,33 ----
                  Oid languageValidator,
                  const char *prosrc,
                  const char *probin,
!                 char prokind,
                  bool security_definer,
                  bool isLeakProof,
                  bool isStrict,
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 1f6c04a..e55ea40 100644
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
*************** extern bool get_func_retset(Oid funcid);
*** 117,123 ****
  extern bool func_strict(Oid funcid);
  extern char func_volatile(Oid funcid);
  extern char func_parallel(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);
--- 117,123 ----
  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_leakproof(Oid funcid);
  extern float4 get_func_cost(Oid funcid);
  extern float4 get_func_rows(Oid funcid);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 77c41b2..fa8e2fd 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** compile_plperl_function(Oid fn_oid, bool
*** 2832,2838 ****
           * Get the required information for input conversion of the
           * return value.
           ************************************************************/
!         if (!is_trigger && !is_event_trigger && procStruct->prorettype)
          {
              Oid            rettype = procStruct->prorettype;

--- 2832,2839 ----
           * Get the required information for input conversion of the
           * return value.
           ************************************************************/
!         if (!is_trigger && !is_event_trigger &&
!             procStruct->prokind != PROKIND_PROCEDURE)
          {
              Oid            rettype = procStruct->prorettype;

diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d07a16a..6fbbc4a 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** do_compile(FunctionCallInfo fcinfo,
*** 275,280 ****
--- 275,281 ----
      bool        isnull;
      char       *proc_source;
      HeapTuple    typeTup;
+     Form_pg_type typeStruct;
      PLpgSQL_variable *var;
      PLpgSQL_rec *rec;
      int            i;
*************** do_compile(FunctionCallInfo fcinfo,
*** 365,370 ****
--- 366,373 ----
      else
          function->fn_is_trigger = PLPGSQL_NOT_TRIGGER;

+     function->fn_prokind = procStruct->prokind;
+
      /*
       * Initialize the compiler, particularly the namespace stack.  The
       * outermost namespace contains function parameters and other special
*************** do_compile(FunctionCallInfo fcinfo,
*** 529,538 ****
              /*
               * Lookup the function's return type
               */
-             if (rettypeid)
-             {
-                 Form_pg_type typeStruct;
-
                  typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettypeid));
                  if (!HeapTupleIsValid(typeTup))
                      elog(ERROR, "cache lookup failed for type %u", rettypeid);
--- 532,537 ----
*************** do_compile(FunctionCallInfo fcinfo,
*** 577,583 ****
                  }

                  ReleaseSysCache(typeTup);
-             }
              break;

          case PLPGSQL_DML_TRIGGER:
--- 576,581 ----
*************** plpgsql_compile_inline(char *proc_source
*** 890,895 ****
--- 888,894 ----
      function->fn_retset = false;
      function->fn_retistuple = false;
      function->fn_retisdomain = false;
+     function->fn_prokind = PROKIND_FUNCTION;
      /* a bit of hardwired knowledge about type VOID here */
      function->fn_retbyval = true;
      function->fn_rettyplen = sizeof(int32);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 4ff87e0..297aa3e 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** plpgsql_exec_function(PLpgSQL_function *
*** 573,579 ****
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
      rc = exec_stmt_block(&estate, func->action);
!     if (rc != PLPGSQL_RC_RETURN && func->fn_rettype)
      {
          estate.err_stmt = NULL;
          estate.err_text = NULL;
--- 573,579 ----
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
      rc = exec_stmt_block(&estate, func->action);
!     if (rc != PLPGSQL_RC_RETURN)
      {
          estate.err_stmt = NULL;
          estate.err_text = NULL;
*************** plpgsql_exec_function(PLpgSQL_function *
*** 617,625 ****
      }
      else if (!estate.retisnull)
      {
!         if (!func->fn_rettype)
              ereport(ERROR,
!                     (errmsg("cannot return a value from a procedure")));

          /*
           * Cast result value to function's declared result type, and copy it
--- 617,626 ----
      }
      else if (!estate.retisnull)
      {
!         if (func->fn_prokind == PROKIND_PROCEDURE)
              ereport(ERROR,
!                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!                      errmsg("cannot return a value from a procedure")));

          /*
           * Cast result value to function's declared result type, and copy it
*************** exec_stmt_return(PLpgSQL_execstate *esta
*** 2956,2964 ****
      /*
       * Special hack for function returning VOID: instead of NULL, return a
       * non-null VOID value.  This is of dubious importance but is kept for
!      * backwards compatibility.
       */
!     if (estate->fn_rettype == VOIDOID)
      {
          estate->retval = (Datum) 0;
          estate->retisnull = false;
--- 2957,2966 ----
      /*
       * Special hack for function returning VOID: instead of NULL, return a
       * non-null VOID value.  This is of dubious importance but is kept for
!      * backwards compatibility.  We don't do it for procedures, though.
       */
!     if (estate->fn_rettype == VOIDOID &&
!         estate->func->fn_prokind != PROKIND_PROCEDURE)
      {
          estate->retval = (Datum) 0;
          estate->retisnull = false;
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 688fbd6..697ead0 100644
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
***************
*** 16,21 ****
--- 16,22 ----
  #include "postgres.h"

  #include "catalog/namespace.h"
+ #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "parser/parser.h"
  #include "parser/parse_type.h"
*************** make_return_stmt(int location)
*** 3137,3143 ****
                       parser_errposition(yylloc)));
          new->retvarno = plpgsql_curr_compile->out_param_varno;
      }
!     else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
      {
          if (yylex() != ';')
              ereport(ERROR,
--- 3138,3145 ----
                       parser_errposition(yylloc)));
          new->retvarno = plpgsql_curr_compile->out_param_varno;
      }
!     else if (plpgsql_curr_compile->fn_rettype == VOIDOID &&
!              plpgsql_curr_compile->fn_prokind != PROKIND_PROCEDURE)
      {
          if (yylex() != ';')
              ereport(ERROR,
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 26a7344..dd59036 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_function
*** 918,923 ****
--- 918,924 ----
      bool        fn_retisdomain;
      bool        fn_retset;
      bool        fn_readonly;
+     char        fn_prokind;

      int            fn_nargs;
      int            fn_argvarnos[FUNC_MAX_ARGS];
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index 4e06413..82cc3f2 100644
*** a/src/pl/plpython/plpy_procedure.c
--- b/src/pl/plpython/plpy_procedure.c
*************** PLy_procedure_create(HeapTuple procTup,
*** 188,194 ****
          proc->fn_tid = procTup->t_self;
          proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
          proc->is_setof = procStruct->proretset;
!         proc->is_procedure = (procStruct->prorettype == InvalidOid);
          proc->src = NULL;
          proc->argnames = NULL;
          proc->args = NULL;
--- 188,194 ----
          proc->fn_tid = procTup->t_self;
          proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
          proc->is_setof = procStruct->proretset;
!         proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
          proc->src = NULL;
          proc->argnames = NULL;
          proc->args = NULL;
*************** PLy_procedure_create(HeapTuple procTup,
*** 208,214 ****
           * get information required for output conversion of the return value,
           * but only if this isn't a trigger or procedure.
           */
!         if (!is_trigger && procStruct->prorettype)
          {
              Oid            rettype = procStruct->prorettype;
              HeapTuple    rvTypeTup;
--- 208,214 ----
           * get information required for output conversion of the return value,
           * but only if this isn't a trigger or procedure.
           */
!         if (!is_trigger && procStruct->prokind != PROKIND_PROCEDURE)
          {
              Oid            rettype = procStruct->prorettype;
              HeapTuple    rvTypeTup;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 5df4dfd..2eb6c33 100644
*** a/src/pl/tcl/pltcl.c
--- b/src/pl/tcl/pltcl.c
*************** compile_pltcl_function(Oid fn_oid, Oid t
*** 1523,1531 ****
           * Get the required information for input conversion of the
           * return value.
           ************************************************************/
!         prodesc->fn_is_procedure = (procStruct->prorettype == InvalidOid);

!         if (!is_trigger && !is_event_trigger && procStruct->prorettype)
          {
              Oid            rettype = procStruct->prorettype;

--- 1523,1531 ----
           * Get the required information for input conversion of the
           * return value.
           ************************************************************/
!         prodesc->fn_is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);

!         if (!is_trigger && !is_event_trigger && !prodesc->fn_is_procedure)
          {
              Oid            rettype = procStruct->prorettype;

diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 44356de..3e40df1 100644
*** a/src/test/regress/expected/alter_generic.out
--- b/src/test/regress/expected/alter_generic.out
*************** ERROR:  must be owner of function alt_ag
*** 83,103 ****
  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, 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 | proisagg |       rolname
! ----------+-----------+------------+----------+---------------------
!  alt_nsp1 | alt_agg2  | integer    | t        | regress_alter_user2
!  alt_nsp1 | alt_agg3  | integer    | t        | regress_alter_user1
!  alt_nsp1 | alt_agg4  | integer    | t        | regress_alter_user2
!  alt_nsp1 | alt_func2 | integer    | f        | regress_alter_user2
!  alt_nsp1 | alt_func3 | integer    | f        | regress_alter_user1
!  alt_nsp1 | alt_func4 | integer    | f        | regress_alter_user2
!  alt_nsp2 | alt_agg2  | integer    | t        | regress_alter_user3
!  alt_nsp2 | alt_func2 | integer    | f        | regress_alter_user3
  (8 rows)

  --
--- 83,103 ----
  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
    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_user2
!  alt_nsp1 | alt_agg3  | integer    | a       | regress_alter_user1
!  alt_nsp1 | alt_agg4  | integer    | a       | regress_alter_user2
!  alt_nsp1 | alt_func2 | integer    | f       | regress_alter_user2
!  alt_nsp1 | alt_func3 | integer    | f       | regress_alter_user1
!  alt_nsp1 | alt_func4 | integer    | f       | regress_alter_user2
!  alt_nsp2 | alt_agg2  | integer    | a       | regress_alter_user3
!  alt_nsp2 | alt_func2 | integer    | f       | regress_alter_user3
  (8 rows)

  --
diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out
index 5ff1e0d..3cdd92c 100644
*** a/src/test/regress/expected/create_function_3.out
--- b/src/test/regress/expected/create_function_3.out
*************** ERROR:  could not find a function named
*** 271,276 ****
--- 271,285 ----
  DROP FUNCTION functest_b_2;  -- error, ambiguous
  ERROR:  function name "functest_b_2" is not unique
  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 type
+ DETAIL:  "functest1" is a function.
+ CREATE OR REPLACE PROCEDURE functest1(a int) LANGUAGE SQL AS 'SELECT $1';
+ ERROR:  cannot change routine type
+ DETAIL:  "functest1" is a function.
+ DROP FUNCTION functest1(a int);
  -- Cleanups
  DROP SCHEMA temp_func_test CASCADE;
  NOTICE:  drop cascades to 16 other objects
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 6616cc1..01608d2 100644
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
*************** WHERE p1.prolang = 0 OR p1.prorettype =
*** 74,79 ****
--- 74,80 ----
         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
         provolatile NOT IN ('i', 's', 'v') OR
         proparallel NOT IN ('s', 'r', 'u');
   oid | proname
*************** WHERE prosrc IS NULL OR prosrc = '' OR p
*** 88,97 ****
  -----+---------
  (0 rows)

! -- proiswindow shouldn't be set together with proisagg or proretset
  SELECT p1.oid, p1.proname
  FROM pg_proc AS p1
! WHERE proiswindow AND (proisagg OR proretset);
   oid | proname
  -----+---------
  (0 rows)
--- 89,98 ----
  -----+---------
  (0 rows)

! -- proretset should only be set for normal functions
  SELECT p1.oid, p1.proname
  FROM pg_proc AS p1
! WHERE proretset AND prokind != 'f';
   oid | proname
  -----+---------
  (0 rows)
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 154,162 ****
  WHERE p1.oid < p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     (p1.proisagg = false OR p2.proisagg = false) AND
      (p1.prolang != p2.prolang OR
!      p1.proisagg != p2.proisagg OR
       p1.prosecdef != p2.prosecdef OR
       p1.proleakproof != p2.proleakproof OR
       p1.proisstrict != p2.proisstrict OR
--- 155,163 ----
  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
      (p1.prolang != p2.prolang OR
!      p1.prokind != p2.prokind OR
       p1.prosecdef != p2.prosecdef OR
       p1.proleakproof != p2.proleakproof OR
       p1.proisstrict != p2.proisstrict OR
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 182,188 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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)
--- 183,189 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.prorettype < p2.prorettype)
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 198,204 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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])
--- 199,205 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.proargtypes[0] < p2.proargtypes[0])
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 216,222 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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])
--- 217,223 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.proargtypes[1] < p2.proargtypes[1])
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 233,239 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[2] < p2.proargtypes[2])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 234,240 ----
  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
      (p1.proargtypes[2] < p2.proargtypes[2])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 246,252 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[3] < p2.proargtypes[3])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 247,253 ----
  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
      (p1.proargtypes[3] < p2.proargtypes[3])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 259,265 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[4] < p2.proargtypes[4])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 260,266 ----
  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
      (p1.proargtypes[4] < p2.proargtypes[4])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 271,277 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[5] < p2.proargtypes[5])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 272,278 ----
  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
      (p1.proargtypes[5] < p2.proargtypes[5])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 283,289 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[6] < p2.proargtypes[6])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 284,290 ----
  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
      (p1.proargtypes[6] < p2.proargtypes[6])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 295,301 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[7] < p2.proargtypes[7])
  ORDER BY 1, 2;
   proargtypes | proargtypes
--- 296,302 ----
  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
      (p1.proargtypes[7] < p2.proargtypes[7])
  ORDER BY 1, 2;
   proargtypes | proargtypes
*************** WHERE aggfnoid = 0 OR aggtransfn = 0 OR
*** 1292,1306 ****
  SELECT a.aggfnoid::oid, p.proname
  FROM pg_aggregate as a, pg_proc as p
  WHERE a.aggfnoid = p.oid AND
!     (NOT p.proisagg OR p.proretset OR p.pronargs < a.aggnumdirectargs);
   aggfnoid | proname
  ----------+---------
  (0 rows)

! -- Make sure there are no proisagg pg_proc entries without matches.
  SELECT oid, proname
  FROM pg_proc as p
! WHERE p.proisagg AND
      NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid);
   oid | proname
  -----+---------
--- 1293,1307 ----
  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);
   aggfnoid | proname
  ----------+---------
  (0 rows)

! -- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches.
  SELECT oid, proname
  FROM pg_proc as p
! WHERE p.prokind = 'a' AND
      NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid);
   oid | proname
  -----+---------
*************** ORDER BY 1, 2;
*** 1639,1645 ****
  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.proisagg AND p2.proisagg AND
      array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
  ORDER BY 1;
       oid      |   oid
--- 1640,1646 ----
  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
      array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
  ORDER BY 1;
       oid      |   oid
*************** ORDER BY 1;
*** 1650,1656 ****
  -- For the same reason, built-in aggregates with default arguments are no good.
  SELECT oid, proname
  FROM pg_proc AS p
! WHERE proisagg AND proargdefaults IS NOT NULL;
   oid | proname
  -----+---------
  (0 rows)
--- 1651,1657 ----
  -- 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;
   oid | proname
  -----+---------
  (0 rows)
*************** WHERE proisagg AND proargdefaults IS NOT
*** 1660,1666 ****
  -- 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 proisagg AND provariadic != 0 AND a.aggkind = 'n';
   oid | proname
  -----+---------
  (0 rows)
--- 1661,1667 ----
  -- 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';
   oid | proname
  -----+---------
  (0 rows)
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 5acb92f..d7eff6c 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** UNION ALL
*** 1521,1529 ****
   SELECT l.objoid,
      l.classoid,
      l.objsubid,
!         CASE
!             WHEN (pro.proisagg = true) THEN 'aggregate'::text
!             WHEN (pro.proisagg = false) THEN 'function'::text
              ELSE NULL::text
          END AS objtype,
      pro.pronamespace AS objnamespace,
--- 1521,1531 ----
   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
          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 96be6e7..fd43f23 100644
*** a/src/test/regress/sql/alter_generic.sql
--- b/src/test/regress/sql/alter_generic.sql
*************** ALTER AGGREGATE alt_agg2(int) SET SCHEMA
*** 81,87 ****

  RESET SESSION AUTHORIZATION;

! 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')
--- 81,87 ----

  RESET SESSION AUTHORIZATION;

! SELECT n.nspname, proname, prorettype::regtype, prokind, 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 fbdf831..8f209d5 100644
*** a/src/test/regress/sql/create_function_3.sql
--- b/src/test/regress/sql/create_function_3.sql
*************** DROP FUNCTION functest_b_1;  -- error, n
*** 175,180 ****
--- 175,188 ----
  DROP FUNCTION functest_b_2;  -- error, ambiguous


+ -- 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';
+ CREATE OR REPLACE PROCEDURE functest1(a int) LANGUAGE SQL AS 'SELECT $1';
+ DROP FUNCTION functest1(a int);
+
+
  -- Cleanups
  DROP SCHEMA temp_func_test CASCADE;
  DROP USER regress_unpriv_user;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e8fdf84..a593d37 100644
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
*************** WHERE p1.prolang = 0 OR p1.prorettype =
*** 82,87 ****
--- 82,88 ----
         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
         provolatile NOT IN ('i', 's', 'v') OR
         proparallel NOT IN ('s', 'r', 'u');

*************** SELECT p1.oid, p1.proname
*** 90,99 ****
  FROM pg_proc as p1
  WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-';

! -- proiswindow shouldn't be set together with proisagg or proretset
  SELECT p1.oid, p1.proname
  FROM pg_proc AS p1
! 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.
--- 91,100 ----
  FROM pg_proc as p1
  WHERE prosrc IS NULL OR prosrc = '' OR prosrc = '-';

! -- proretset should only be set for normal functions
  SELECT p1.oid, p1.proname
  FROM pg_proc AS p1
! WHERE proretset AND prokind != 'f';

  -- currently, no built-in functions should be SECURITY DEFINER;
  -- this might change in future, but there will probably never be many.
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 140,148 ****
  WHERE p1.oid < p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     (p1.proisagg = false OR p2.proisagg = false) AND
      (p1.prolang != p2.prolang OR
!      p1.proisagg != p2.proisagg OR
       p1.prosecdef != p2.prosecdef OR
       p1.proleakproof != p2.proleakproof OR
       p1.proisstrict != p2.proisstrict OR
--- 141,149 ----
  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
      (p1.prolang != p2.prolang OR
!      p1.prokind != p2.prokind OR
       p1.prosecdef != p2.prosecdef OR
       p1.proleakproof != p2.proleakproof OR
       p1.proisstrict != p2.proisstrict OR
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 166,172 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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)
--- 167,173 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.prorettype < p2.prorettype)
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 177,183 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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])
--- 178,184 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.proargtypes[0] < p2.proargtypes[0])
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 188,194 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 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])
--- 189,195 ----
  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
      p1.prosrc NOT LIKE E'range\\_constructor_' AND
      p2.prosrc NOT LIKE E'range\\_constructor_' AND
      (p1.proargtypes[1] < p2.proargtypes[1])
*************** FROM pg_proc AS p1, pg_proc AS p2
*** 199,205 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[2] < p2.proargtypes[2])
  ORDER BY 1, 2;

--- 200,206 ----
  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
      (p1.proargtypes[2] < p2.proargtypes[2])
  ORDER BY 1, 2;

*************** FROM pg_proc AS p1, pg_proc AS p2
*** 208,214 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[3] < p2.proargtypes[3])
  ORDER BY 1, 2;

--- 209,215 ----
  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
      (p1.proargtypes[3] < p2.proargtypes[3])
  ORDER BY 1, 2;

*************** FROM pg_proc AS p1, pg_proc AS p2
*** 217,223 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[4] < p2.proargtypes[4])
  ORDER BY 1, 2;

--- 218,224 ----
  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
      (p1.proargtypes[4] < p2.proargtypes[4])
  ORDER BY 1, 2;

*************** FROM pg_proc AS p1, pg_proc AS p2
*** 226,232 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[5] < p2.proargtypes[5])
  ORDER BY 1, 2;

--- 227,233 ----
  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
      (p1.proargtypes[5] < p2.proargtypes[5])
  ORDER BY 1, 2;

*************** FROM pg_proc AS p1, pg_proc AS p2
*** 235,241 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[6] < p2.proargtypes[6])
  ORDER BY 1, 2;

--- 236,242 ----
  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
      (p1.proargtypes[6] < p2.proargtypes[6])
  ORDER BY 1, 2;

*************** FROM pg_proc AS p1, pg_proc AS p2
*** 244,250 ****
  WHERE p1.oid != p2.oid AND
      p1.prosrc = p2.prosrc AND
      p1.prolang = 12 AND p2.prolang = 12 AND
!     NOT p1.proisagg AND NOT p2.proisagg AND
      (p1.proargtypes[7] < p2.proargtypes[7])
  ORDER BY 1, 2;

--- 245,251 ----
  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
      (p1.proargtypes[7] < p2.proargtypes[7])
  ORDER BY 1, 2;

*************** WHERE aggfnoid = 0 OR aggtransfn = 0 OR
*** 804,816 ****
  SELECT a.aggfnoid::oid, p.proname
  FROM pg_aggregate as a, pg_proc as p
  WHERE a.aggfnoid = p.oid AND
!     (NOT p.proisagg OR p.proretset OR p.pronargs < a.aggnumdirectargs);

! -- Make sure there are no proisagg pg_proc entries without matches.

  SELECT oid, proname
  FROM pg_proc as p
! 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.
--- 805,817 ----
  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);

! -- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches.

  SELECT oid, proname
  FROM pg_proc as p
! WHERE p.prokind = 'a' 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.
*************** ORDER BY 1, 2;
*** 1089,1095 ****
  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.proisagg AND p2.proisagg AND
      array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
  ORDER BY 1;

--- 1090,1096 ----
  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
      array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
  ORDER BY 1;

*************** ORDER BY 1;
*** 1097,1103 ****

  SELECT oid, proname
  FROM pg_proc AS p
! 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
--- 1098,1104 ----

  SELECT oid, proname
  FROM pg_proc AS p
! WHERE prokind = 'a' 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
*************** WHERE proisagg AND proargdefaults IS NOT
*** 1105,1111 ****

  SELECT p.oid, proname
  FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
! WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';


  -- **************** pg_opfamily ****************
--- 1106,1112 ----

  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';


  -- **************** pg_opfamily ****************
#! /usr/bin/perl

# Usage: fix_proc_data.pl <pg_proc.h >new_pg_proc.h

use strict;
use warnings;

while (<>)
{
    if (m/^DATA/) {
    my @fields = split;
    my $proisagg = $fields[13];
    my $proiswindow = $fields[14];
    my $prokind;
    if ($proisagg eq 'f' && $proiswindow eq 'f') {
        $prokind = 'f';
    } elsif ($proisagg eq 't' && $proiswindow eq 'f') {
        $prokind = 'a';
    } elsif ($proisagg eq 'f' && $proiswindow eq 't') {
        $prokind = 'w';
    } else {
        die "bad proisagg/proiswindow";
    }
    s/ $proisagg $proiswindow / $prokind / || die "substitution failed";
    }
    print;
}

Attachment

pgsql-hackers by date:

Previous
From: David Steele
Date:
Subject: 2018-03 Commitfest starts tomorrow
Next
From: Peter Eisentraut
Date:
Subject: SET TRANSACTION in PL/pgSQL