postgresql/src/backend/catalog/pg_aggregate.c

280 lines
8.0 KiB
C

/*-------------------------------------------------------------------------
*
* pg_aggregate.c
* routines to support manipulation of the pg_aggregate relation
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.35 2000/07/17 03:04:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
/* ----------------
* AggregateCreate
*
* aggregates overloading has been added. Instead of the full
* overload support we have for functions, aggregate overloading only
* applies to exact basetype matches. That is, we don't check the
* inheritance hierarchy
*
* OLD COMMENTS:
* 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
* is created and inserted in the aggregate relation.
* All types and functions must have been defined
* prior to defining the aggregate.
*
* ---------------
*/
void
AggregateCreate(char *aggName,
char *aggtransfnName,
char *aggfinalfnName,
char *aggbasetypeName,
char *aggtranstypeName,
char *agginitval)
{
Relation aggdesc;
HeapTuple tup;
char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */
Oid basetype;
Oid transtype;
Oid finaltype;
Oid fnArgs[FUNC_MAX_ARGS];
int nargs;
NameData aname;
TupleDesc tupDesc;
int i;
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
/* sanity checks */
if (!aggName)
elog(ERROR, "AggregateCreate: no aggregate name supplied");
if (!aggtransfnName)
elog(ERROR, "AggregateCreate: aggregate must have a transition function");
/*
* Handle the aggregate's base type (input data type). This can be
* specified as 'ANY' for a data-independent transition function,
* such as COUNT(*).
*/
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(aggbasetypeName),
0, 0, 0);
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;
}
/* make sure there is no existing agg of same name and base type */
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
ObjectIdGetDatum(basetype),
0, 0);
if (HeapTupleIsValid(tup))
elog(ERROR,
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
aggName, aggbasetypeName);
/* 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))
{
fnArgs[1] = basetype;
nargs = 2;
}
else
{
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");
}
/* handle finalfn, if supplied */
if (aggfinalfnName)
{
fnArgs[0] = transtype;
fnArgs[1] = 0;
tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(aggfinalfnName),
Int32GetDatum(1),
PointerGetDatum(fnArgs),
0);
if (!HeapTupleIsValid(tup))
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
finalfn = tup->t_data->t_oid;
proc = (Form_pg_proc) GETSTRUCT(tup);
finaltype = proc->prorettype;
Assert(OidIsValid(finalfn));
}
else
{
/*
* If no finalfn, aggregate result type is type of the state value
*/
finaltype = transtype;
}
Assert(OidIsValid(finaltype));
/* initialize nulls and values */
for (i = 0; i < Natts_pg_aggregate; i++)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
namestrcpy(&aname, aggName);
values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
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));
else
nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
tupDesc = aggdesc->rd_att;
if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
values,
nulls)))
elog(ERROR, "AggregateCreate: heap_formtuple failed");
if (!OidIsValid(heap_insert(aggdesc, tup)))
elog(ERROR, "AggregateCreate: heap_insert failed");
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);
}
heap_close(aggdesc, RowExclusiveLock);
}
Datum
AggNameGetInitVal(char *aggName, Oid basetype, bool *isNull)
{
HeapTuple tup;
Oid transtype,
typinput,
typelem;
Datum textInitVal;
char *strInitVal;
Datum initVal;
Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull));
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
ObjectIdGetDatum(basetype),
0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
aggName);
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype;
/*
* 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);
if (*isNull)
return (Datum) 0;
strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
tup = SearchSysCacheTuple(TYPEOID,
ObjectIdGetDatum(transtype),
0, 0, 0);
if (!HeapTupleIsValid(tup))
{
pfree(strInitVal);
elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
}
typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
initVal = OidFunctionCall3(typinput,
CStringGetDatum(strInitVal),
ObjectIdGetDatum(typelem),
Int32GetDatum(-1));
pfree(strInitVal);
return initVal;
}