Commit 01bde4fa authored by Andrew Gierth's avatar Andrew Gierth

Implement OR REPLACE option for CREATE AGGREGATE.

Aggregates have acquired a dozen or so optional attributes in recent
years for things like parallel query and moving-aggregate mode; the
lack of an OR REPLACE option to add or change these for an existing
agg makes extension upgrades gratuitously hard. Rectify.
parent f2004f19
...@@ -21,7 +21,7 @@ PostgreSQL documentation ...@@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <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>, SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable> STYPE = <replaceable class="parameter">state_data_type</replaceable>
[ , SSPACE = <replaceable class="parameter">state_data_size</replaceable> ] [ , SSPACE = <replaceable class="parameter">state_data_size</replaceable> ]
...@@ -44,7 +44,7 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replacea ...@@ -44,7 +44,7 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replacea
[ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] [ , 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> [ , ... ] ) ( 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>, SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable> STYPE = <replaceable class="parameter">state_data_type</replaceable>
...@@ -59,7 +59,7 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replac ...@@ -59,7 +59,7 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replac
<phrase>or the old syntax</phrase> <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>, BASETYPE = <replaceable class="parameter">base_type</replaceable>,
SFUNC = <replaceable class="parameter">sfunc</replaceable>, SFUNC = <replaceable class="parameter">sfunc</replaceable>,
STYPE = <replaceable class="parameter">state_data_type</replaceable> STYPE = <replaceable class="parameter">state_data_type</replaceable>
...@@ -88,12 +88,21 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( ...@@ -88,12 +88,21 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> (
<title>Description</title> <title>Description</title>
<para> <para>
<command>CREATE AGGREGATE</command> defines a new aggregate <command>CREATE AGGREGATE</command> defines a new aggregate function.
function. Some basic and commonly-used aggregate functions are <command>CREATE OR REPLACE AGGREGATE</command> will either define a new
included with the distribution; they are documented in <xref aggregate function or replace an existing definition. Some basic and
linkend="functions-aggregate"/>. If one defines new types or needs commonly-used aggregate functions are included with the distribution; they
an aggregate function not already provided, then <command>CREATE are documented in <xref linkend="functions-aggregate"/>. If one defines new
AGGREGATE</command> can be used to provide the desired features. 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>
<para> <para>
......
...@@ -45,6 +45,7 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types, ...@@ -45,6 +45,7 @@ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
ObjectAddress ObjectAddress
AggregateCreate(const char *aggName, AggregateCreate(const char *aggName,
Oid aggNamespace, Oid aggNamespace,
bool replace,
char aggKind, char aggKind,
int numArgs, int numArgs,
int numDirectArgs, int numDirectArgs,
...@@ -77,8 +78,10 @@ AggregateCreate(const char *aggName, ...@@ -77,8 +78,10 @@ AggregateCreate(const char *aggName,
{ {
Relation aggdesc; Relation aggdesc;
HeapTuple tup; HeapTuple tup;
HeapTuple oldtup;
bool nulls[Natts_pg_aggregate]; bool nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate];
bool replaces[Natts_pg_aggregate];
Form_pg_proc proc; Form_pg_proc proc;
Oid transfn; Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */ Oid finalfn = InvalidOid; /* can be omitted */
...@@ -609,7 +612,7 @@ AggregateCreate(const char *aggName, ...@@ -609,7 +612,7 @@ AggregateCreate(const char *aggName,
myself = ProcedureCreate(aggName, myself = ProcedureCreate(aggName,
aggNamespace, aggNamespace,
false, /* no replacement */ replace, /* maybe replacement */
false, /* doesn't return a set */ false, /* doesn't return a set */
finaltype, /* returnType */ finaltype, /* returnType */
GetUserId(), /* proowner */ GetUserId(), /* proowner */
...@@ -648,6 +651,7 @@ AggregateCreate(const char *aggName, ...@@ -648,6 +651,7 @@ AggregateCreate(const char *aggName,
{ {
nulls[i] = false; nulls[i] = false;
values[i] = (Datum) NULL; values[i] = (Datum) NULL;
replaces[i] = true;
} }
values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind); values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind);
...@@ -678,8 +682,51 @@ AggregateCreate(const char *aggName, ...@@ -678,8 +682,51 @@ AggregateCreate(const char *aggName,
else else
nulls[Anum_pg_aggregate_aggminitval - 1] = true; nulls[Anum_pg_aggregate_aggminitval - 1] = true;
tup = heap_form_tuple(tupDesc, values, nulls); if (replace)
CatalogTupleInsert(aggdesc, tup); 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); table_close(aggdesc, RowExclusiveLock);
...@@ -688,6 +735,10 @@ AggregateCreate(const char *aggName, ...@@ -688,6 +735,10 @@ AggregateCreate(const char *aggName,
* made by ProcedureCreate). Note: we don't need an explicit dependency * made by ProcedureCreate). Note: we don't need an explicit dependency
* on aggTransType since we depend on it indirectly through transfn. * on aggTransType since we depend on it indirectly through transfn.
* Likewise for aggmTransType using the mtransfunc, if it exists. * 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 */ /* Depends on transition function */
......
...@@ -404,7 +404,9 @@ ProcedureCreate(const char *procedureName, ...@@ -404,7 +404,9 @@ ProcedureCreate(const char *procedureName,
errdetail("\"%s\" is a window function.", procedureName) : errdetail("\"%s\" is a window function.", procedureName) :
0))); 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 * Not okay to change the return type of the existing proc, since
...@@ -421,7 +423,7 @@ ProcedureCreate(const char *procedureName, ...@@ -421,7 +423,7 @@ ProcedureCreate(const char *procedureName,
prokind == PROKIND_PROCEDURE prokind == PROKIND_PROCEDURE
? errmsg("cannot change whether a procedure has output parameters") ? errmsg("cannot change whether a procedure has output parameters")
: errmsg("cannot change return type of existing function"), : 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.", errhint("Use %s %s first.",
dropcmd, dropcmd,
format_procedure(oldproc->oid)))); format_procedure(oldproc->oid))));
......
...@@ -54,7 +54,12 @@ static char extractModify(DefElem *defel); ...@@ -54,7 +54,12 @@ static char extractModify(DefElem *defel);
* "parameters" is a list of DefElem representing the agg's definition clauses. * "parameters" is a list of DefElem representing the agg's definition clauses.
*/ */
ObjectAddress 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; char *aggName;
Oid aggNamespace; Oid aggNamespace;
...@@ -436,6 +441,7 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List ...@@ -436,6 +441,7 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List
*/ */
return AggregateCreate(aggName, /* aggregate name */ return AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */ aggNamespace, /* namespace */
replace,
aggKind, aggKind,
numArgs, numArgs,
numDirectArgs, numDirectArgs,
......
...@@ -3372,6 +3372,7 @@ _copyDefineStmt(const DefineStmt *from) ...@@ -3372,6 +3372,7 @@ _copyDefineStmt(const DefineStmt *from)
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_NODE_FIELD(definition); COPY_NODE_FIELD(definition);
COPY_SCALAR_FIELD(if_not_exists); COPY_SCALAR_FIELD(if_not_exists);
COPY_SCALAR_FIELD(replace);
return newnode; return newnode;
} }
......
...@@ -1265,6 +1265,7 @@ _equalDefineStmt(const DefineStmt *a, const DefineStmt *b) ...@@ -1265,6 +1265,7 @@ _equalDefineStmt(const DefineStmt *a, const DefineStmt *b)
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(definition); COMPARE_NODE_FIELD(definition);
COMPARE_SCALAR_FIELD(if_not_exists); COMPARE_SCALAR_FIELD(if_not_exists);
COMPARE_SCALAR_FIELD(replace);
return true; return true;
} }
......
...@@ -5618,25 +5618,27 @@ CreateAssertionStmt: ...@@ -5618,25 +5618,27 @@ CreateAssertionStmt:
*****************************************************************************/ *****************************************************************************/
DefineStmt: DefineStmt:
CREATE AGGREGATE func_name aggr_args definition CREATE opt_or_replace AGGREGATE func_name aggr_args definition
{ {
DefineStmt *n = makeNode(DefineStmt); DefineStmt *n = makeNode(DefineStmt);
n->kind = OBJECT_AGGREGATE; n->kind = OBJECT_AGGREGATE;
n->oldstyle = false; n->oldstyle = false;
n->defnames = $3; n->replace = $2;
n->args = $4; n->defnames = $4;
n->definition = $5; n->args = $5;
n->definition = $6;
$$ = (Node *)n; $$ = (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 */ /* old-style (pre-8.2) syntax for CREATE AGGREGATE */
DefineStmt *n = makeNode(DefineStmt); DefineStmt *n = makeNode(DefineStmt);
n->kind = OBJECT_AGGREGATE; n->kind = OBJECT_AGGREGATE;
n->oldstyle = true; n->oldstyle = true;
n->defnames = $3; n->replace = $2;
n->defnames = $4;
n->args = NIL; n->args = NIL;
n->definition = $4; n->definition = $5;
$$ = (Node *)n; $$ = (Node *)n;
} }
| CREATE OPERATOR any_operator definition | CREATE OPERATOR any_operator definition
......
...@@ -1237,7 +1237,8 @@ ProcessUtilitySlow(ParseState *pstate, ...@@ -1237,7 +1237,8 @@ ProcessUtilitySlow(ParseState *pstate,
address = address =
DefineAggregate(pstate, stmt->defnames, stmt->args, DefineAggregate(pstate, stmt->defnames, stmt->args,
stmt->oldstyle, stmt->oldstyle,
stmt->definition); stmt->definition,
stmt->replace);
break; break;
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
Assert(stmt->args == NIL); Assert(stmt->args == NIL);
......
...@@ -142,6 +142,7 @@ typedef FormData_pg_aggregate *Form_pg_aggregate; ...@@ -142,6 +142,7 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
extern ObjectAddress AggregateCreate(const char *aggName, extern ObjectAddress AggregateCreate(const char *aggName,
Oid aggNamespace, Oid aggNamespace,
bool replace,
char aggKind, char aggKind,
int numArgs, int numArgs,
int numDirectArgs, int numDirectArgs,
......
...@@ -94,7 +94,7 @@ extern void UpdateStatisticsForTypeChange(Oid statsOid, ...@@ -94,7 +94,7 @@ extern void UpdateStatisticsForTypeChange(Oid statsOid,
/* commands/aggregatecmds.c */ /* commands/aggregatecmds.c */
extern ObjectAddress DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, extern ObjectAddress DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle,
List *parameters); List *parameters, bool replace);
/* commands/opclasscmds.c */ /* commands/opclasscmds.c */
extern ObjectAddress DefineOpClass(CreateOpClassStmt *stmt); extern ObjectAddress DefineOpClass(CreateOpClassStmt *stmt);
......
...@@ -2532,6 +2532,7 @@ typedef struct DefineStmt ...@@ -2532,6 +2532,7 @@ typedef struct DefineStmt
List *args; /* a list of TypeName (if needed) */ List *args; /* a list of TypeName (if needed) */
List *definition; /* a list of DefElem */ List *definition; /* a list of DefElem */
bool if_not_exists; /* just do nothing if it already exists? */ bool if_not_exists; /* just do nothing if it already exists? */
bool replace; /* replace if already exists? */
} DefineStmt; } DefineStmt;
/* ---------------------- /* ----------------------
......
...@@ -160,6 +160,77 @@ WHERE aggfnoid = 'myavg'::REGPROC; ...@@ -160,6 +160,77 @@ WHERE aggfnoid = 'myavg'::REGPROC;
myavg | numeric_avg_accum | numeric_avg_combine | internal | numeric_avg_serialize | numeric_avg_deserialize | s myavg | numeric_avg_accum | numeric_avg_combine | internal | numeric_avg_serialize | numeric_avg_deserialize | s
(1 row) (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); DROP AGGREGATE myavg (numeric);
-- invalid: bad parallel-safety marking -- invalid: bad parallel-safety marking
CREATE AGGREGATE mysum (int) CREATE AGGREGATE mysum (int)
......
...@@ -174,6 +174,71 @@ WHERE aggfnoid = 'myavg'::REGPROC; ...@@ -174,6 +174,71 @@ WHERE aggfnoid = 'myavg'::REGPROC;
DROP AGGREGATE myavg (numeric); 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 -- invalid: bad parallel-safety marking
CREATE AGGREGATE mysum (int) CREATE AGGREGATE mysum (int)
( (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment