From b90baa875491f48568edf7ed7f41b5850a9c289d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 20:18:30 +0000 Subject: [PATCH v0] Enable altering prosupport function of an aggregate The prosupport has been settable only through CREATE/ALTER FUNCTION, that rejects aggregates, so an aggregate could acquire a support function only as a pg_proc.dat entry or direct catalog update. Extensions should have more convenient way by a DDL command. Add SUPPORT to CREATE AGGREGATE's option list, and accept it in ALTER AGGREGATE. ALTER FUNCTION continues to reject them, as it does for every other property. Setting a support function requires superuser, as the equivalent function clauses do, and records a normal dependency on it. CREATE OR REPLACE AGGREGATE without SUPPORT clears it, matching CREATE OR REPLACE FUNCTION; as for functions, ALTER cannot clear it. The restriction that SUPPORT is the only alterable property of an aggregate is enforced by the grammar for ALTER AGGREGATE. --- doc/src/sgml/ref/alter_aggregate.sgml | 20 ++++++++++++++++++++ doc/src/sgml/ref/create_aggregate.sgml | 21 +++++++++++++++++++++ src/backend/catalog/pg_aggregate.c | 6 ++++-- src/backend/commands/aggregatecmds.c | 11 ++++++++++- src/backend/commands/functioncmds.c | 20 ++++++++++++++++++-- src/backend/parser/gram.y | 15 ++++++++++++++- src/backend/tcop/utility.c | 3 +++ src/bin/psql/tab-complete.in.c | 2 +- src/include/catalog/pg_aggregate.h | 3 ++- src/include/commands/defrem.h | 1 + 10 files changed, 94 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/ref/alter_aggregate.sgml b/doc/src/sgml/ref/alter_aggregate.sgml index d0a39ba7b5e..4079a6f7a13 100644 --- a/doc/src/sgml/ref/alter_aggregate.sgml +++ b/doc/src/sgml/ref/alter_aggregate.sgml @@ -25,6 +25,7 @@ ALTER AGGREGATE name ( aggregate_signatu ALTER AGGREGATE name ( aggregate_signature ) OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER AGGREGATE name ( aggregate_signature ) SET SCHEMA new_schema +ALTER AGGREGATE name ( aggregate_signature ) SUPPORT support_function where aggregate_signature is: @@ -53,6 +54,8 @@ ALTER AGGREGATE name ( aggregate_signatu the owner doesn't do anything you couldn't do by dropping and recreating the aggregate function. However, a superuser can alter ownership of any aggregate function anyway.) + To set the aggregate function's planner support function, you must be + superuser. @@ -133,6 +136,23 @@ ALTER AGGREGATE name ( aggregate_signatu + + + SUPPORT support_function + + + Set or change the planner support function to use for this aggregate. + See for details. You must be + superuser to use this option. + + + + This option cannot be used to remove the support function altogether, + since it must name a new support function. Use CREATE OR + REPLACE AGGREGATE if you need to do that. + + + diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index 0472ac2e874..b5829091abc 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -42,6 +42,7 @@ CREATE [ OR REPLACE ] AGGREGATE nameminitial_condition ] [ , SORTOP = sort_operator ] [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] + [ , SUPPORT = support_function ] ) CREATE [ OR REPLACE ] AGGREGATE name ( [ [ argmode ] [ argname ] arg_data_type [ , ... ] ] @@ -54,6 +55,7 @@ CREATE [ OR REPLACE ] AGGREGATE nameinitial_condition ] [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] + [ , SUPPORT = support_function ] [ , HYPOTHETICAL ] ) @@ -644,6 +646,25 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; + + SUPPORT = support_function + + + The name (optionally schema-qualified) of a planner support + function to use for this aggregate. See + for details. + You must be superuser to use this option. + + + + Note that this is unrelated to the aggregate's transition, final and + other helper functions described above, which implement the aggregate + itself; a planner support function instead advises the planner about + calls to the aggregate. + + + + HYPOTHETICAL diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 243b952b9cc..9cb28e0e569 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -30,6 +30,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "utils/regproc.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -74,7 +75,8 @@ AggregateCreate(const char *aggName, int32 aggmTransSpace, const char *agginitval, const char *aggminitval, - char proparallel) + char proparallel, + Oid prosupport) { Relation aggdesc; HeapTuple tup; @@ -639,7 +641,7 @@ AggregateCreate(const char *aggName, PointerGetDatum(NULL), /* trftypes */ NIL, /* trfoids */ PointerGetDatum(NULL), /* proconfig */ - InvalidOid, /* no prosupport */ + prosupport, /* planner support function */ 1, /* procost */ 0); /* prorows */ procOid = myself.objectId; diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 41b45dc6402..f8e51c1d81c 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -82,6 +82,8 @@ DefineAggregate(ParseState *pstate, char *initval = NULL; char *minitval = NULL; char *parallel = NULL; + DefElem *supportItem = NULL; + Oid prosupport = InvalidOid; int numArgs; int numDirectArgs = 0; oidvector *parameterTypes; @@ -186,6 +188,8 @@ DefineAggregate(ParseState *pstate, minitval = defGetString(defel); else if (strcmp(defel->defname, "parallel") == 0) parallel = defGetString(defel); + else if (strcmp(defel->defname, "support") == 0) + supportItem = defel; else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), @@ -434,6 +438,10 @@ DefineAggregate(ParseState *pstate, errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE"))); } + /* interpret_func_support handles the privilege check */ + if (supportItem) + prosupport = interpret_func_support(supportItem); + /* * Most of the argument-checking is done inside of AggregateCreate */ @@ -468,7 +476,8 @@ DefineAggregate(ParseState *pstate, mtransSpace, /* transition space */ initval, /* initial condition */ minitval, /* initial condition */ - proparallel); /* parallel safe? */ + proparallel, /* parallel safe? */ + prosupport); /* planner support function */ } /* diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 3afd762e9dc..3b9bc0b58b4 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -684,7 +684,12 @@ update_proconfig_value(ArrayType *a, List *set_items) return a; } -static Oid +/* + * Interpret a SUPPORT clause, and return the OID of the named planner + * support function. This is shared by CREATE/ALTER FUNCTION and + * CREATE/ALTER AGGREGATE; it also enforces the privilege check. + */ +Oid interpret_func_support(DefElem *defel) { List *procName = defGetQualifiedName(defel); @@ -1397,7 +1402,14 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype, NameListToString(stmt->func->objname)); - if (procForm->prokind == PROKIND_AGGREGATE) + /* + * An aggregate's properties belong to CREATE AGGREGATE, except for the + * planner support function, which has no other DDL home. Only ALTER + * AGGREGATE can set that, and its grammar admits no other action, as + * asserted below. + */ + if (procForm->prokind == PROKIND_AGGREGATE && + stmt->objtype != OBJECT_AGGREGATE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", @@ -1425,6 +1437,10 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt) elog(ERROR, "option \"%s\" not recognized", defel->defname); } + /* SUPPORT is the only thing the grammar lets us do to an aggregate */ + Assert(procForm->prokind != PROKIND_AGGREGATE || + (support_item != NULL && list_length(stmt->actions) == 1)); + if (volatility_item) procForm->provolatile = interpret_func_volatility(volatility_item); if (strict_item) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 17035fb4d15..498c75e4c42 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9164,12 +9164,15 @@ table_func_column_list: ; /***************************************************************************** - * ALTER FUNCTION / ALTER PROCEDURE / ALTER ROUTINE + * ALTER FUNCTION / ALTER PROCEDURE / ALTER ROUTINE / ALTER AGGREGATE * * RENAME and OWNER subcommands are already provided by the generic * ALTER infrastructure, here we just specify alterations that can * only be applied to functions. * + * For aggregates, SUPPORT is the only alterable property; the rest of an + * aggregate's definition belongs to CREATE AGGREGATE. + * *****************************************************************************/ AlterFunctionStmt: ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict @@ -9199,6 +9202,16 @@ AlterFunctionStmt: n->actions = $4; $$ = (Node *) n; } + | ALTER AGGREGATE aggregate_with_argtypes SUPPORT any_name + { + AlterFunctionStmt *n = makeNode(AlterFunctionStmt); + + n->objtype = OBJECT_AGGREGATE; + n->func = $3; + n->actions = list_make1(makeDefElem("support", + (Node *) $5, @4)); + $$ = (Node *) n; + } ; alterfunc_opt_list: diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 4d33fcb5e9d..37dd3d4f8f2 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -2759,6 +2759,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_ROUTINE: tag = CMDTAG_ALTER_ROUTINE; break; + case OBJECT_AGGREGATE: + tag = CMDTAG_ALTER_AGGREGATE; + break; default: tag = CMDTAG_UNKNOWN; } diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 190fff7ea0e..da849e5b1af 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2244,7 +2244,7 @@ match_previous_words(int pattern_id, else if (Matches("ALTER", "AGGREGATE", MatchAny, MatchAny)) { if (ends_with(prev_wd, ')')) - COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA"); + COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SUPPORT"); else COMPLETE_WITH_FUNCTION_ARG(prev2_wd); } diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 2b4f5dae5f2..ccf6377e9ee 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -181,6 +181,7 @@ extern ObjectAddress AggregateCreate(const char *aggName, int32 aggmTransSpace, const char *agginitval, const char *aggminitval, - char proparallel); + char proparallel, + Oid prosupport); #endif /* PG_AGGREGATE_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 574f860bdd2..48dadcf1bc7 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -58,6 +58,7 @@ extern void GetOperatorFromCompareType(Oid opclass, Oid rhstype, CompareType cmp extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt); extern void RemoveFunctionById(Oid funcOid); extern ObjectAddress AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt); +extern Oid interpret_func_support(DefElem *defel); extern ObjectAddress CreateCast(CreateCastStmt *stmt); extern ObjectAddress CreateTransform(CreateTransformStmt *stmt); extern void IsThereFunctionInNamespace(const char *proname, int pronargs, -- 2.55.0