<refsynopsisdiv>
<synopsis>
-CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
+CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable>
[ , SSPACE = <replaceable class="parameter">state_data_size</replaceable> ]
[ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
)
-CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ]
+CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ]
ORDER BY [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable>
<phrase>or the old syntax</phrase>
-CREATE AGGREGATE <replaceable class="parameter">name</replaceable> (
+CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable> (
BASETYPE = <replaceable class="parameter">base_type</replaceable>,
SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable>
<title>Description</title>
<para>
- <command>CREATE AGGREGATE</command> defines a new aggregate
- function. Some basic and commonly-used aggregate functions are
- included with the distribution; they are documented in <xref
- linkend="functions-aggregate"/>. If one defines new types or needs
- an aggregate function not already provided, then <command>CREATE
- AGGREGATE</command> can be used to provide the desired features.
+ <command>CREATE AGGREGATE</command> defines a new aggregate function.
+ <command>CREATE OR REPLACE AGGREGATE</command> will either define a new
+ aggregate function or replace an existing definition. Some basic and
+ commonly-used aggregate functions are included with the distribution; they
+ are documented in <xref linkend="functions-aggregate"/>. If one defines new
+ types or needs an aggregate function not already provided, then
+ <command>CREATE AGGREGATE</command> can be used to provide the desired
+ features.
+ </para>
+
+ <para>
+ When replacing an existing definition, the argument types, result type,
+ and number of direct arguments may not be changed. Also, the new definition
+ must be of the same kind (ordinary aggregate, ordered-set aggregate, or
+ hypothetical-set aggregate) as the old one.
</para>
<para>
ObjectAddress
AggregateCreate(const char *aggName,
Oid aggNamespace,
+ bool replace,
char aggKind,
int numArgs,
int numDirectArgs,
{
Relation aggdesc;
HeapTuple tup;
+ HeapTuple oldtup;
bool nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
+ bool replaces[Natts_pg_aggregate];
Form_pg_proc proc;
Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */
myself = ProcedureCreate(aggName,
aggNamespace,
- false, /* no replacement */
+ replace, /* maybe replacement */
false, /* doesn't return a set */
finaltype, /* returnType */
GetUserId(), /* proowner */
{
nulls[i] = false;
values[i] = (Datum) NULL;
+ replaces[i] = true;
}
values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind);
else
nulls[Anum_pg_aggregate_aggminitval - 1] = true;
- tup = heap_form_tuple(tupDesc, values, nulls);
- CatalogTupleInsert(aggdesc, tup);
+ if (replace)
+ oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
+ else
+ oldtup = NULL;
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ Form_pg_aggregate oldagg = (Form_pg_aggregate) GETSTRUCT(oldtup);
+
+ /*
+ * If we're replacing an existing entry, we need to validate that
+ * we're not changing anything that would break callers.
+ * Specifically we must not change aggkind or aggnumdirectargs,
+ * which affect how an aggregate call is treated in parse
+ * analysis.
+ */
+ if (aggKind != oldagg->aggkind)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change routine kind"),
+ (oldagg->aggkind == AGGKIND_NORMAL ?
+ errdetail("\"%s\" is an ordinary aggregate function.", aggName) :
+ oldagg->aggkind == AGGKIND_ORDERED_SET ?
+ errdetail("\"%s\" is an ordered-set aggregate.", aggName) :
+ oldagg->aggkind == AGGKIND_HYPOTHETICAL ?
+ errdetail("\"%s\" is a hypothetical-set aggregate.", aggName) :
+ 0)));
+ if (numDirectArgs != oldagg->aggnumdirectargs)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot change number of direct args of an aggregate function")));
+
+ replaces[Anum_pg_aggregate_aggfnoid - 1] = false;
+ replaces[Anum_pg_aggregate_aggkind - 1] = false;
+ replaces[Anum_pg_aggregate_aggnumdirectargs - 1] = false;
+
+ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
+ CatalogTupleUpdate(aggdesc, &tup->t_self, tup);
+ ReleaseSysCache(oldtup);
+ }
+ else
+ {
+ tup = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(aggdesc, tup);
+ }
table_close(aggdesc, RowExclusiveLock);
* made by ProcedureCreate). Note: we don't need an explicit dependency
* on aggTransType since we depend on it indirectly through transfn.
* Likewise for aggmTransType using the mtransfunc, if it exists.
+ *
+ * If we're replacing an existing definition, ProcedureCreate deleted all
+ * our existing dependencies, so we have to do the same things here either
+ * way.
*/
/* Depends on transition function */
errdetail("\"%s\" is a window function.", procedureName) :
0)));
- dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" : "DROP FUNCTION");
+ dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" :
+ prokind == PROKIND_AGGREGATE ? "DROP AGGREGATE" :
+ "DROP FUNCTION");
/*
* Not okay to change the return type of the existing proc, since
prokind == PROKIND_PROCEDURE
? errmsg("cannot change whether a procedure has output parameters")
: errmsg("cannot change return type of existing function"),
- /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
+ /* translator: first %s is DROP FUNCTION, DROP PROCEDURE or DROP AGGREGATE */
errhint("Use %s %s first.",
dropcmd,
format_procedure(oldproc->oid))));
* "parameters" is a list of DefElem representing the agg's definition clauses.
*/
ObjectAddress
-DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List *parameters)
+DefineAggregate(ParseState *pstate,
+ List *name,
+ List *args,
+ bool oldstyle,
+ List *parameters,
+ bool replace)
{
char *aggName;
Oid aggNamespace;
*/
return AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */
+ replace,
aggKind,
numArgs,
numDirectArgs,
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(definition);
COPY_SCALAR_FIELD(if_not_exists);
+ COPY_SCALAR_FIELD(replace);
return newnode;
}
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(definition);
COMPARE_SCALAR_FIELD(if_not_exists);
+ COMPARE_SCALAR_FIELD(replace);
return true;
}
*****************************************************************************/
DefineStmt:
- CREATE AGGREGATE func_name aggr_args definition
+ CREATE opt_or_replace AGGREGATE func_name aggr_args definition
{
DefineStmt *n = makeNode(DefineStmt);
n->kind = OBJECT_AGGREGATE;
n->oldstyle = false;
- n->defnames = $3;
- n->args = $4;
- n->definition = $5;
+ n->replace = $2;
+ n->defnames = $4;
+ n->args = $5;
+ n->definition = $6;
$$ = (Node *)n;
}
- | CREATE AGGREGATE func_name old_aggr_definition
+ | CREATE opt_or_replace AGGREGATE func_name old_aggr_definition
{
/* old-style (pre-8.2) syntax for CREATE AGGREGATE */
DefineStmt *n = makeNode(DefineStmt);
n->kind = OBJECT_AGGREGATE;
n->oldstyle = true;
- n->defnames = $3;
+ n->replace = $2;
+ n->defnames = $4;
n->args = NIL;
- n->definition = $4;
+ n->definition = $5;
$$ = (Node *)n;
}
| CREATE OPERATOR any_operator definition
address =
DefineAggregate(pstate, stmt->defnames, stmt->args,
stmt->oldstyle,
- stmt->definition);
+ stmt->definition,
+ stmt->replace);
break;
case OBJECT_OPERATOR:
Assert(stmt->args == NIL);
extern ObjectAddress AggregateCreate(const char *aggName,
Oid aggNamespace,
+ bool replace,
char aggKind,
int numArgs,
int numDirectArgs,
/* commands/aggregatecmds.c */
extern ObjectAddress DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle,
- List *parameters);
+ List *parameters, bool replace);
/* commands/opclasscmds.c */
extern ObjectAddress DefineOpClass(CreateOpClassStmt *stmt);
List *args; /* a list of TypeName (if needed) */
List *definition; /* a list of DefElem */
bool if_not_exists; /* just do nothing if it already exists? */
+ bool replace; /* replace if already exists? */
} DefineStmt;
/* ----------------------
myavg | numeric_avg_accum | numeric_avg_combine | internal | numeric_avg_serialize | numeric_avg_deserialize | s
(1 row)
+DROP AGGREGATE myavg (numeric);
+-- create or replace aggregate
+CREATE AGGREGATE myavg (numeric)
+(
+ stype = internal,
+ sfunc = numeric_avg_accum,
+ finalfunc = numeric_avg
+);
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = internal,
+ sfunc = numeric_avg_accum,
+ finalfunc = numeric_avg,
+ serialfunc = numeric_avg_serialize,
+ deserialfunc = numeric_avg_deserialize,
+ combinefunc = numeric_avg_combine,
+ finalfunc_modify = shareable -- just to test a non-default setting
+);
+-- Ensure all these functions made it into the catalog again
+SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype,
+ aggserialfn, aggdeserialfn, aggfinalmodify
+FROM pg_aggregate
+WHERE aggfnoid = 'myavg'::REGPROC;
+ aggfnoid | aggtransfn | aggcombinefn | aggtranstype | aggserialfn | aggdeserialfn | aggfinalmodify
+----------+-------------------+---------------------+--------------+-----------------------+-------------------------+----------------
+ myavg | numeric_avg_accum | numeric_avg_combine | internal | numeric_avg_serialize | numeric_avg_deserialize | s
+(1 row)
+
+-- can change stype:
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add
+);
+SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype,
+ aggserialfn, aggdeserialfn, aggfinalmodify
+FROM pg_aggregate
+WHERE aggfnoid = 'myavg'::REGPROC;
+ aggfnoid | aggtransfn | aggcombinefn | aggtranstype | aggserialfn | aggdeserialfn | aggfinalmodify
+----------+-------------+--------------+--------------+-------------+---------------+----------------
+ myavg | numeric_add | - | numeric | - | - | r
+(1 row)
+
+-- can't change return type:
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add,
+ finalfunc = numeric_out
+);
+ERROR: cannot change return type of existing function
+HINT: Use DROP AGGREGATE myavg(numeric) first.
+-- can't change to a different kind:
+CREATE OR REPLACE AGGREGATE myavg (order by numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add
+);
+ERROR: cannot change routine kind
+DETAIL: "myavg" is an ordinary aggregate function.
+-- can't change plain function to aggregate:
+create function sum4(int8,int8,int8,int8) returns int8 as
+'select $1 + $2 + $3 + $4' language sql strict immutable;
+CREATE OR REPLACE AGGREGATE sum3 (int8,int8,int8)
+(
+ stype = int8,
+ sfunc = sum4
+);
+ERROR: cannot change routine kind
+DETAIL: "sum3" is a function.
+drop function sum4(int8,int8,int8,int8);
DROP AGGREGATE myavg (numeric);
-- invalid: bad parallel-safety marking
CREATE AGGREGATE mysum (int)
DROP AGGREGATE myavg (numeric);
+-- create or replace aggregate
+CREATE AGGREGATE myavg (numeric)
+(
+ stype = internal,
+ sfunc = numeric_avg_accum,
+ finalfunc = numeric_avg
+);
+
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = internal,
+ sfunc = numeric_avg_accum,
+ finalfunc = numeric_avg,
+ serialfunc = numeric_avg_serialize,
+ deserialfunc = numeric_avg_deserialize,
+ combinefunc = numeric_avg_combine,
+ finalfunc_modify = shareable -- just to test a non-default setting
+);
+
+-- Ensure all these functions made it into the catalog again
+SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype,
+ aggserialfn, aggdeserialfn, aggfinalmodify
+FROM pg_aggregate
+WHERE aggfnoid = 'myavg'::REGPROC;
+
+-- can change stype:
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add
+);
+SELECT aggfnoid, aggtransfn, aggcombinefn, aggtranstype::regtype,
+ aggserialfn, aggdeserialfn, aggfinalmodify
+FROM pg_aggregate
+WHERE aggfnoid = 'myavg'::REGPROC;
+
+-- can't change return type:
+CREATE OR REPLACE AGGREGATE myavg (numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add,
+ finalfunc = numeric_out
+);
+
+-- can't change to a different kind:
+CREATE OR REPLACE AGGREGATE myavg (order by numeric)
+(
+ stype = numeric,
+ sfunc = numeric_add
+);
+
+-- can't change plain function to aggregate:
+create function sum4(int8,int8,int8,int8) returns int8 as
+'select $1 + $2 + $3 + $4' language sql strict immutable;
+
+CREATE OR REPLACE AGGREGATE sum3 (int8,int8,int8)
+(
+ stype = int8,
+ sfunc = sum4
+);
+
+drop function sum4(int8,int8,int8,int8);
+
+DROP AGGREGATE myavg (numeric);
+
-- invalid: bad parallel-safety marking
CREATE AGGREGATE mysum (int)
(