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)
|
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: