1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* pg_aggregate.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* routines to support manipulation of the pg_aggregate relation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2000-07-17 05:05:41 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.35 2000/07/17 03:04:43 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/catname.h"
|
1999-11-22 18:56:41 +01:00
|
|
|
#include "catalog/indexing.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_aggregate.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "miscadmin.h"
|
2000-07-17 05:05:41 +02:00
|
|
|
#include "parser/parse_coerce.h"
|
|
|
|
#include "parser/parse_func.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-11-04 00:27:08 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* ----------------
|
|
|
|
* AggregateCreate
|
|
|
|
*
|
|
|
|
* aggregates overloading has been added. Instead of the full
|
|
|
|
* overload support we have for functions, aggregate overloading only
|
1997-09-07 07:04:48 +02:00
|
|
|
* applies to exact basetype matches. That is, we don't check the
|
2000-03-26 21:43:58 +02:00
|
|
|
* inheritance hierarchy
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* OLD COMMENTS:
|
1997-09-07 07:04:48 +02:00
|
|
|
* Currently, redefining aggregates using the same name is not
|
|
|
|
* supported. In such a case, a warning is printed that the
|
|
|
|
* aggregate already exists. If such is not the case, a new tuple
|
2000-07-17 05:05:41 +02:00
|
|
|
* is created and inserted in the aggregate relation.
|
1997-09-07 07:04:48 +02:00
|
|
|
* All types and functions must have been defined
|
|
|
|
* prior to defining the aggregate.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AggregateCreate(char *aggName,
|
2000-07-17 05:05:41 +02:00
|
|
|
char *aggtransfnName,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *aggfinalfnName,
|
|
|
|
char *aggbasetypeName,
|
2000-07-17 05:05:41 +02:00
|
|
|
char *aggtranstypeName,
|
|
|
|
char *agginitval)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation aggdesc;
|
|
|
|
HeapTuple tup;
|
|
|
|
char nulls[Natts_pg_aggregate];
|
|
|
|
Datum values[Natts_pg_aggregate];
|
|
|
|
Form_pg_proc proc;
|
2000-07-17 05:05:41 +02:00
|
|
|
Oid transfn;
|
|
|
|
Oid finalfn = InvalidOid; /* can be omitted */
|
|
|
|
Oid basetype;
|
|
|
|
Oid transtype;
|
|
|
|
Oid finaltype;
|
2000-01-10 18:14:46 +01:00
|
|
|
Oid fnArgs[FUNC_MAX_ARGS];
|
2000-07-17 05:05:41 +02:00
|
|
|
int nargs;
|
1998-09-01 06:40:42 +02:00
|
|
|
NameData aname;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc;
|
2000-07-17 05:05:41 +02:00
|
|
|
int i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-01-10 18:14:46 +01:00
|
|
|
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* sanity checks */
|
|
|
|
if (!aggName)
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: no aggregate name supplied");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
if (!aggtransfnName)
|
|
|
|
elog(ERROR, "AggregateCreate: aggregate must have a transition function");
|
2000-03-26 21:43:58 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/*
|
|
|
|
* Handle the aggregate's base type (input data type). This can be
|
|
|
|
* specified as 'ANY' for a data-independent transition function,
|
|
|
|
* such as COUNT(*).
|
|
|
|
*/
|
1999-11-22 18:56:41 +01:00
|
|
|
tup = SearchSysCacheTuple(TYPENAME,
|
1997-09-07 07:04:48 +02:00
|
|
|
PointerGetDatum(aggbasetypeName),
|
|
|
|
0, 0, 0);
|
2000-07-17 05:05:41 +02:00
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
basetype = tup->t_data->t_oid;
|
|
|
|
Assert(OidIsValid(basetype));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (strcasecmp(aggbasetypeName, "ANY") != 0)
|
|
|
|
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
|
|
|
aggbasetypeName);
|
|
|
|
basetype = InvalidOid;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-03-26 21:43:58 +02:00
|
|
|
/* make sure there is no existing agg of same name and base type */
|
|
|
|
tup = SearchSysCacheTuple(AGGNAME,
|
|
|
|
PointerGetDatum(aggName),
|
2000-07-17 05:05:41 +02:00
|
|
|
ObjectIdGetDatum(basetype),
|
2000-03-26 21:43:58 +02:00
|
|
|
0, 0);
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
elog(ERROR,
|
|
|
|
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
|
|
|
|
aggName, aggbasetypeName);
|
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/* handle transtype */
|
|
|
|
tup = SearchSysCacheTuple(TYPENAME,
|
|
|
|
PointerGetDatum(aggtranstypeName),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
|
|
|
aggtranstypeName);
|
|
|
|
transtype = tup->t_data->t_oid;
|
|
|
|
Assert(OidIsValid(transtype));
|
|
|
|
|
|
|
|
/* handle transfn */
|
|
|
|
fnArgs[0] = transtype;
|
|
|
|
if (OidIsValid(basetype))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-07-17 05:05:41 +02:00
|
|
|
fnArgs[1] = basetype;
|
|
|
|
nargs = 2;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-07-17 05:05:41 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-07-17 05:05:41 +02:00
|
|
|
nargs = 1;
|
|
|
|
}
|
|
|
|
tup = SearchSysCacheTuple(PROCNAME,
|
|
|
|
PointerGetDatum(aggtransfnName),
|
|
|
|
Int32GetDatum(nargs),
|
|
|
|
PointerGetDatum(fnArgs),
|
|
|
|
0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
|
|
|
transfn = tup->t_data->t_oid;
|
|
|
|
proc = (Form_pg_proc) GETSTRUCT(tup);
|
|
|
|
if (proc->prorettype != transtype)
|
|
|
|
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
|
|
|
|
aggtransfnName, aggtranstypeName);
|
|
|
|
Assert(OidIsValid(transfn));
|
|
|
|
/*
|
|
|
|
* If the transfn is strict and the initval is NULL, make sure
|
|
|
|
* input type and transtype are the same (or at least binary-
|
|
|
|
* compatible), so that it's OK to use the first input value
|
|
|
|
* as the initial transValue.
|
|
|
|
*/
|
|
|
|
if (((Form_pg_proc) GETSTRUCT(tup))->proisstrict && agginitval == NULL)
|
|
|
|
{
|
|
|
|
if (basetype != transtype &&
|
|
|
|
! IS_BINARY_COMPATIBLE(basetype, transtype))
|
|
|
|
elog(ERROR, "AggregateCreate: must not omit initval when transfn is strict and transtype is not compatible with input type");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/* handle finalfn, if supplied */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (aggfinalfnName)
|
|
|
|
{
|
2000-07-17 05:05:41 +02:00
|
|
|
fnArgs[0] = transtype;
|
|
|
|
fnArgs[1] = 0;
|
1999-11-22 18:56:41 +01:00
|
|
|
tup = SearchSysCacheTuple(PROCNAME,
|
1997-09-07 07:04:48 +02:00
|
|
|
PointerGetDatum(aggfinalfnName),
|
2000-07-17 05:05:41 +02:00
|
|
|
Int32GetDatum(1),
|
1997-09-07 07:04:48 +02:00
|
|
|
PointerGetDatum(fnArgs),
|
|
|
|
0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
2000-07-17 05:05:41 +02:00
|
|
|
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
|
|
|
finalfn = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
proc = (Form_pg_proc) GETSTRUCT(tup);
|
2000-07-17 05:05:41 +02:00
|
|
|
finaltype = proc->prorettype;
|
|
|
|
Assert(OidIsValid(finalfn));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-03-26 21:43:58 +02:00
|
|
|
else
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2000-07-17 05:05:41 +02:00
|
|
|
* If no finalfn, aggregate result type is type of the state value
|
2000-03-26 21:43:58 +02:00
|
|
|
*/
|
2000-07-17 05:05:41 +02:00
|
|
|
finaltype = transtype;
|
2000-03-26 21:43:58 +02:00
|
|
|
}
|
2000-07-17 05:05:41 +02:00
|
|
|
Assert(OidIsValid(finaltype));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* initialize nulls and values */
|
|
|
|
for (i = 0; i < Natts_pg_aggregate; i++)
|
|
|
|
{
|
|
|
|
nulls[i] = ' ';
|
|
|
|
values[i] = (Datum) NULL;
|
|
|
|
}
|
1998-04-01 17:35:33 +02:00
|
|
|
namestrcpy(&aname, aggName);
|
|
|
|
values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
|
1999-02-03 22:18:02 +01:00
|
|
|
values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
|
2000-07-17 05:05:41 +02:00
|
|
|
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
|
|
|
|
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
|
|
|
|
values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(basetype);
|
|
|
|
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(transtype);
|
|
|
|
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(finaltype);
|
|
|
|
|
|
|
|
if (agginitval)
|
|
|
|
values[Anum_pg_aggregate_agginitval - 1] =
|
|
|
|
DirectFunctionCall1(textin, CStringGetDatum(agginitval));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2000-07-17 05:05:41 +02:00
|
|
|
nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
tupDesc = aggdesc->rd_att;
|
|
|
|
if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
|
|
|
|
values,
|
|
|
|
nulls)))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: heap_formtuple failed");
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!OidIsValid(heap_insert(aggdesc, tup)))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: heap_insert failed");
|
1999-11-22 18:56:41 +01:00
|
|
|
|
|
|
|
if (RelationGetForm(aggdesc)->relhasindex)
|
|
|
|
{
|
|
|
|
Relation idescs[Num_pg_aggregate_indices];
|
|
|
|
|
|
|
|
CatalogOpenIndices(Num_pg_aggregate_indices, Name_pg_aggregate_indices, idescs);
|
|
|
|
CatalogIndexInsert(idescs, Num_pg_aggregate_indices, aggdesc, tup);
|
|
|
|
CatalogCloseIndices(Num_pg_aggregate_indices, idescs);
|
|
|
|
}
|
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(aggdesc, RowExclusiveLock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-05-30 06:25:00 +02:00
|
|
|
Datum
|
2000-07-17 05:05:41 +02:00
|
|
|
AggNameGetInitVal(char *aggName, Oid basetype, bool *isNull)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tup;
|
2000-01-15 23:43:25 +01:00
|
|
|
Oid transtype,
|
|
|
|
typinput,
|
|
|
|
typelem;
|
2000-07-06 01:12:09 +02:00
|
|
|
Datum textInitVal;
|
2000-05-30 06:25:00 +02:00
|
|
|
char *strInitVal;
|
|
|
|
Datum initVal;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(PointerIsValid(aggName));
|
|
|
|
Assert(PointerIsValid(isNull));
|
1999-09-18 21:08:25 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
tup = SearchSysCacheTuple(AGGNAME,
|
|
|
|
PointerGetDatum(aggName),
|
1998-08-19 04:04:17 +02:00
|
|
|
ObjectIdGetDatum(basetype),
|
1997-09-07 07:04:48 +02:00
|
|
|
0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggName);
|
2000-07-17 05:05:41 +02:00
|
|
|
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
/*
|
|
|
|
* initval is potentially null, so don't try to access it as a struct
|
|
|
|
* field. Must do it the hard way with SysCacheGetAttr.
|
|
|
|
*/
|
|
|
|
textInitVal = SysCacheGetAttr(AGGNAME, tup,
|
|
|
|
Anum_pg_aggregate_agginitval,
|
|
|
|
isNull);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (*isNull)
|
2000-07-17 05:05:41 +02:00
|
|
|
return (Datum) 0;
|
1999-09-18 21:08:25 +02:00
|
|
|
|
2000-07-17 05:05:41 +02:00
|
|
|
strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-11-22 18:56:41 +01:00
|
|
|
tup = SearchSysCacheTuple(TYPEOID,
|
1998-08-19 04:04:17 +02:00
|
|
|
ObjectIdGetDatum(transtype),
|
1997-09-07 07:04:48 +02:00
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
pfree(strInitVal);
|
2000-04-16 06:16:55 +02:00
|
|
|
elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-01-15 23:43:25 +01:00
|
|
|
typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
|
|
|
|
typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
|
|
|
|
|
2000-05-30 06:25:00 +02:00
|
|
|
initVal = OidFunctionCall3(typinput,
|
|
|
|
CStringGetDatum(strInitVal),
|
|
|
|
ObjectIdGetDatum(typelem),
|
|
|
|
Int32GetDatum(-1));
|
2000-01-15 23:43:25 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
pfree(strInitVal);
|
1998-09-01 05:29:17 +02:00
|
|
|
return initVal;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|