@@ -45,6 +45,7 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
4545ObjectAddress
4646AggregateCreate (const char * aggName ,
4747 Oid aggNamespace ,
48+ bool replace ,
4849 char aggKind ,
4950 int numArgs ,
5051 int numDirectArgs ,
@@ -77,8 +78,10 @@ AggregateCreate(const char *aggName,
7778{
7879 Relation aggdesc ;
7980 HeapTuple tup ;
81+ HeapTuple oldtup ;
8082 bool nulls [Natts_pg_aggregate ];
8183 Datum values [Natts_pg_aggregate ];
84+ bool replaces [Natts_pg_aggregate ];
8285 Form_pg_proc proc ;
8386 Oid transfn ;
8487 Oid finalfn = InvalidOid ; /* can be omitted */
@@ -609,7 +612,7 @@ AggregateCreate(const char *aggName,
609612
610613 myself = ProcedureCreate (aggName ,
611614 aggNamespace ,
612- false , /* no replacement */
615+ replace , /* maybe replacement */
613616 false, /* doesn't return a set */
614617 finaltype , /* returnType */
615618 GetUserId (), /* proowner */
@@ -648,6 +651,7 @@ AggregateCreate(const char *aggName,
648651 {
649652 nulls [i ] = false;
650653 values [i ] = (Datum ) NULL ;
654+ replaces [i ] = true;
651655 }
652656 values [Anum_pg_aggregate_aggfnoid - 1 ] = ObjectIdGetDatum (procOid );
653657 values [Anum_pg_aggregate_aggkind - 1 ] = CharGetDatum (aggKind );
@@ -678,8 +682,51 @@ AggregateCreate(const char *aggName,
678682 else
679683 nulls [Anum_pg_aggregate_aggminitval - 1 ] = true;
680684
681- tup = heap_form_tuple (tupDesc , values , nulls );
682- CatalogTupleInsert (aggdesc , tup );
685+ if (replace )
686+ oldtup = SearchSysCache1 (AGGFNOID , ObjectIdGetDatum (procOid ));
687+ else
688+ oldtup = NULL ;
689+
690+ if (HeapTupleIsValid (oldtup ))
691+ {
692+ Form_pg_aggregate oldagg = (Form_pg_aggregate ) GETSTRUCT (oldtup );
693+
694+ /*
695+ * If we're replacing an existing entry, we need to validate that
696+ * we're not changing anything that would break callers.
697+ * Specifically we must not change aggkind or aggnumdirectargs,
698+ * which affect how an aggregate call is treated in parse
699+ * analysis.
700+ */
701+ if (aggKind != oldagg -> aggkind )
702+ ereport (ERROR ,
703+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
704+ errmsg ("cannot change routine kind" ),
705+ (oldagg -> aggkind == AGGKIND_NORMAL ?
706+ errdetail ("\"%s\" is an ordinary aggregate function." , aggName ) :
707+ oldagg -> aggkind == AGGKIND_ORDERED_SET ?
708+ errdetail ("\"%s\" is an ordered-set aggregate." , aggName ) :
709+ oldagg -> aggkind == AGGKIND_HYPOTHETICAL ?
710+ errdetail ("\"%s\" is a hypothetical-set aggregate." , aggName ) :
711+ 0 )));
712+ if (numDirectArgs != oldagg -> aggnumdirectargs )
713+ ereport (ERROR ,
714+ (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION ),
715+ errmsg ("cannot change number of direct args of an aggregate function" )));
716+
717+ replaces [Anum_pg_aggregate_aggfnoid - 1 ] = false;
718+ replaces [Anum_pg_aggregate_aggkind - 1 ] = false;
719+ replaces [Anum_pg_aggregate_aggnumdirectargs - 1 ] = false;
720+
721+ tup = heap_modify_tuple (oldtup , tupDesc , values , nulls , replaces );
722+ CatalogTupleUpdate (aggdesc , & tup -> t_self , tup );
723+ ReleaseSysCache (oldtup );
724+ }
725+ else
726+ {
727+ tup = heap_form_tuple (tupDesc , values , nulls );
728+ CatalogTupleInsert (aggdesc , tup );
729+ }
683730
684731 table_close (aggdesc , RowExclusiveLock );
685732
@@ -688,6 +735,10 @@ AggregateCreate(const char *aggName,
688735 * made by ProcedureCreate). Note: we don't need an explicit dependency
689736 * on aggTransType since we depend on it indirectly through transfn.
690737 * Likewise for aggmTransType using the mtransfunc, if it exists.
738+ *
739+ * If we're replacing an existing definition, ProcedureCreate deleted all
740+ * our existing dependencies, so we have to do the same things here either
741+ * way.
691742 */
692743
693744 /* Depends on transition function */
0 commit comments