postgresql/src/backend/catalog/pg_aggregate.c

333 lines
10 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* pg_aggregate.c
* routines to support manipulation of the pg_aggregate relation
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.28 2000/01/15 22:43:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
1999-07-16 07:00:38 +02:00
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
1999-07-16 07:00:38 +02:00
#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
* 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. The fields
* of this tuple are aggregate name, owner id, 2 transition functions
* (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
* type of data on which aggtransfn1 operates (aggbasetype), return
* types of the two transition functions (aggtranstype1 and
* aggtranstype2), final return type (aggfinaltype), and initial values
* for the two state transition functions (agginitval1 and agginitval2).
* All types and functions must have been defined
* prior to defining the aggregate.
*
* ---------------
*/
void
AggregateCreate(char *aggName,
char *aggtransfn1Name,
char *aggtransfn2Name,
char *aggfinalfnName,
char *aggbasetypeName,
char *aggtransfn1typeName,
char *aggtransfn2typeName,
char *agginitval1,
char *agginitval2)
{
int i;
Relation aggdesc;
HeapTuple tup;
char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
Oid xfn1 = InvalidOid;
Oid xfn2 = InvalidOid;
Oid ffn = InvalidOid;
Oid xbase = InvalidOid;
Oid xret1 = InvalidOid;
Oid xret2 = InvalidOid;
Oid fret = InvalidOid;
Oid fnArgs[FUNC_MAX_ARGS];
NameData aname;
TupleDesc tupDesc;
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
/* sanity checks */
if (!aggName)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: no aggregate name supplied");
if (!aggtransfn1Name && !aggtransfn2Name)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(aggbasetypeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
1998-11-27 20:52:36 +01:00
xbase = tup->t_data->t_oid;
if (aggtransfn1Name)
{
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(aggtransfn1typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: Type '%s' undefined",
aggtransfn1typeName);
1998-11-27 20:52:36 +01:00
xret1 = tup->t_data->t_oid;
fnArgs[0] = xret1;
fnArgs[1] = xbase;
tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(aggtransfn1Name),
Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: '%s('%s', '%s') does not exist",
aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn1Name,
aggtransfn1typeName);
1998-11-27 20:52:36 +01:00
xfn1 = tup->t_data->t_oid;
if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
!OidIsValid(xbase))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
if (aggtransfn2Name)
{
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(aggtransfn2typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: Type '%s' undefined",
aggtransfn2typeName);
1998-11-27 20:52:36 +01:00
xret2 = tup->t_data->t_oid;
fnArgs[0] = xret2;
fnArgs[1] = 0;
tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(aggtransfn2Name),
Int32GetDatum(1),
PointerGetDatum(fnArgs),
0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
aggtransfn2Name, aggtransfn2typeName);
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
aggtransfn2Name, aggtransfn2typeName);
1998-11-27 20:52:36 +01:00
xfn2 = tup->t_data->t_oid;
if (!OidIsValid(xfn2) || !OidIsValid(xret2))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
ObjectIdGetDatum(xbase),
0, 0);
if (HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR,
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
aggName, aggbasetypeName);
/* more sanity checks */
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: Aggregate cannot have final function without both transition functions");
if (aggfinalfnName)
{
fnArgs[0] = xret1;
fnArgs[1] = xret2;
tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(aggfinalfnName),
Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: '%s'('%s','%s') does not exist",
aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
1998-11-27 20:52:36 +01:00
ffn = tup->t_data->t_oid;
proc = (Form_pg_proc) GETSTRUCT(tup);
fret = proc->prorettype;
if (!OidIsValid(ffn) || !OidIsValid(fret))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
}
/*
* If transition function 2 is defined, it must have an initial value,
* whereas transition function 1 does not, which allows man and min
* aggregates to return NULL if they are evaluated on empty sets.
*/
if (OidIsValid(xfn2) && !agginitval2)
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value");
/* 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_aggtransfn1 - 1] = ObjectIdGetDatum(xfn1);
values[Anum_pg_aggregate_aggtransfn2 - 1] = ObjectIdGetDatum(xfn2);
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(ffn);
values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase);
if (!OidIsValid(xfn1))
{
values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret2);
}
else if (!OidIsValid(xfn2))
{
values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret1);
}
else
{
values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(fret);
}
if (agginitval1)
values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
else
nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
if (agginitval2)
values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
else
nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
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");
if (!OidIsValid(heap_insert(aggdesc, tup)))
1998-01-06 20:42:33 +01:00
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);
}
char *
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
{
HeapTuple tup;
Relation aggRel;
int initValAttno;
Oid transtype,
typinput,
typelem;
text *textInitVal;
char *strInitVal,
*initVal;
Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull));
Assert(xfuncno == 1 || xfuncno == 2);
/*
* since we will have to use fastgetattr (in case one or both init vals
* are NULL), we will need to open the relation. Do that first to
* ensure we don't get a stale tuple from the cache.
*/
aggRel = heap_openr(AggregateRelationName, AccessShareLock);
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
ObjectIdGetDatum(basetype),
0, 0);
if (!HeapTupleIsValid(tup))
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
aggName);
if (xfuncno == 1)
{
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
initValAttno = Anum_pg_aggregate_agginitval1;
}
else
{
/* can only be 1 or 2 */
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
initValAttno = Anum_pg_aggregate_agginitval2;
}
textInitVal = (text *) fastgetattr(tup, initValAttno,
1998-09-01 05:29:17 +02:00
RelationGetDescr(aggRel),
isNull);
if (!PointerIsValid(textInitVal))
*isNull = true;
if (*isNull)
{
heap_close(aggRel, AccessShareLock);
1998-09-01 05:29:17 +02:00
return (char *) NULL;
}
strInitVal = textout(textInitVal);
heap_close(aggRel, AccessShareLock);
tup = SearchSysCacheTuple(TYPEOID,
ObjectIdGetDatum(transtype),
0, 0, 0);
if (!HeapTupleIsValid(tup))
{
pfree(strInitVal);
1998-01-06 20:42:33 +01:00
elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
}
typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
initVal = fmgr(typinput, strInitVal, typelem, -1);
pfree(strInitVal);
1998-09-01 05:29:17 +02:00
return initVal;
}