mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
278 lines
7.9 KiB
C
278 lines
7.9 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_aggregate.c
|
|
* routines to support manipulation of the pg_aggregate relation
|
|
*
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.38 2001/03/22 03:59:20 momjian 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(*).
|
|
*/
|
|
basetype = GetSysCacheOid(TYPENAME,
|
|
PointerGetDatum(aggbasetypeName),
|
|
0, 0, 0);
|
|
if (!OidIsValid(basetype))
|
|
{
|
|
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 */
|
|
if (SearchSysCacheExists(AGGNAME,
|
|
PointerGetDatum(aggName),
|
|
ObjectIdGetDatum(basetype),
|
|
0, 0))
|
|
elog(ERROR,
|
|
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
|
|
aggName, aggbasetypeName);
|
|
|
|
/* handle transtype */
|
|
transtype = GetSysCacheOid(TYPENAME,
|
|
PointerGetDatum(aggtranstypeName),
|
|
0, 0, 0);
|
|
if (!OidIsValid(transtype))
|
|
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
|
aggtranstypeName);
|
|
|
|
/* handle transfn */
|
|
fnArgs[0] = transtype;
|
|
if (OidIsValid(basetype))
|
|
{
|
|
fnArgs[1] = basetype;
|
|
nargs = 2;
|
|
}
|
|
else
|
|
nargs = 1;
|
|
tup = SearchSysCache(PROCNAME,
|
|
PointerGetDatum(aggtransfnName),
|
|
Int32GetDatum(nargs),
|
|
PointerGetDatum(fnArgs),
|
|
0);
|
|
if (!HeapTupleIsValid(tup))
|
|
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
|
transfn = tup->t_data->t_oid;
|
|
Assert(OidIsValid(transfn));
|
|
proc = (Form_pg_proc) GETSTRUCT(tup);
|
|
if (proc->prorettype != transtype)
|
|
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
|
|
aggtransfnName, aggtranstypeName);
|
|
|
|
/*
|
|
* 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 (proc->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");
|
|
}
|
|
ReleaseSysCache(tup);
|
|
|
|
/* handle finalfn, if supplied */
|
|
if (aggfinalfnName)
|
|
{
|
|
fnArgs[0] = transtype;
|
|
fnArgs[1] = 0;
|
|
tup = SearchSysCache(PROCNAME,
|
|
PointerGetDatum(aggfinalfnName),
|
|
Int32GetDatum(1),
|
|
PointerGetDatum(fnArgs),
|
|
0);
|
|
if (!HeapTupleIsValid(tup))
|
|
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
|
finalfn = tup->t_data->t_oid;
|
|
Assert(OidIsValid(finalfn));
|
|
proc = (Form_pg_proc) GETSTRUCT(tup);
|
|
finaltype = proc->prorettype;
|
|
ReleaseSysCache(tup);
|
|
}
|
|
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 = SearchSysCache(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)
|
|
{
|
|
ReleaseSysCache(tup);
|
|
return (Datum) 0;
|
|
}
|
|
|
|
strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
tup = SearchSysCache(TYPEOID,
|
|
ObjectIdGetDatum(transtype),
|
|
0, 0, 0);
|
|
if (!HeapTupleIsValid(tup))
|
|
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;
|
|
ReleaseSysCache(tup);
|
|
|
|
initVal = OidFunctionCall3(typinput,
|
|
CStringGetDatum(strInitVal),
|
|
ObjectIdGetDatum(typelem),
|
|
Int32GetDatum(-1));
|
|
|
|
pfree(strInitVal);
|
|
return initVal;
|
|
}
|