@@ -45,6 +45,7 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
45
45
ObjectAddress
46
46
AggregateCreate (const char * aggName ,
47
47
Oid aggNamespace ,
48
+ bool replace ,
48
49
char aggKind ,
49
50
int numArgs ,
50
51
int numDirectArgs ,
@@ -77,8 +78,10 @@ AggregateCreate(const char *aggName,
77
78
{
78
79
Relation aggdesc ;
79
80
HeapTuple tup ;
81
+ HeapTuple oldtup ;
80
82
bool nulls [Natts_pg_aggregate ];
81
83
Datum values [Natts_pg_aggregate ];
84
+ bool replaces [Natts_pg_aggregate ];
82
85
Form_pg_proc proc ;
83
86
Oid transfn ;
84
87
Oid finalfn = InvalidOid ; /* can be omitted */
@@ -609,7 +612,7 @@ AggregateCreate(const char *aggName,
609
612
610
613
myself = ProcedureCreate (aggName ,
611
614
aggNamespace ,
612
- false , /* no replacement */
615
+ replace , /* maybe replacement */
613
616
false, /* doesn't return a set */
614
617
finaltype , /* returnType */
615
618
GetUserId (), /* proowner */
@@ -648,6 +651,7 @@ AggregateCreate(const char *aggName,
648
651
{
649
652
nulls [i ] = false;
650
653
values [i ] = (Datum ) NULL ;
654
+ replaces [i ] = true;
651
655
}
652
656
values [Anum_pg_aggregate_aggfnoid - 1 ] = ObjectIdGetDatum (procOid );
653
657
values [Anum_pg_aggregate_aggkind - 1 ] = CharGetDatum (aggKind );
@@ -678,8 +682,51 @@ AggregateCreate(const char *aggName,
678
682
else
679
683
nulls [Anum_pg_aggregate_aggminitval - 1 ] = true;
680
684
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
+ }
683
730
684
731
table_close (aggdesc , RowExclusiveLock );
685
732
@@ -688,6 +735,10 @@ AggregateCreate(const char *aggName,
688
735
* made by ProcedureCreate). Note: we don't need an explicit dependency
689
736
* on aggTransType since we depend on it indirectly through transfn.
690
737
* 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.
691
742
*/
692
743
693
744
/* Depends on transition function */
0 commit comments