1996-07-09 08:22:35 +02: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
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1998-11-27 20:52:36 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.18 1998/11/27 19:51:50 vadim Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-06 08:31:26 +01:00
|
|
|
#include <postgres.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1996-11-06 08:31:26 +01:00
|
|
|
#include <access/heapam.h>
|
|
|
|
#include <utils/builtins.h>
|
|
|
|
#include <fmgr.h>
|
|
|
|
#include <catalog/catname.h>
|
|
|
|
#include <utils/syscache.h>
|
|
|
|
#include <catalog/pg_operator.h>
|
|
|
|
#include <catalog/pg_proc.h>
|
|
|
|
#include <catalog/pg_type.h>
|
|
|
|
#include <catalog/pg_aggregate.h>
|
|
|
|
#include <miscadmin.h>
|
|
|
|
#ifndef HAVE_MEMMOVE
|
1997-09-07 07:04:48 +02:00
|
|
|
#include <regex/utils.h>
|
1996-11-06 08:31:26 +01:00
|
|
|
#else
|
1997-09-07 07:04:48 +02:00
|
|
|
#include <string.h>
|
1996-11-06 08:31:26 +01:00
|
|
|
#endif
|
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
|
1996-07-09 08:22:35 +02:00
|
|
|
* the inheritance hierarchy
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* 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.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* ---------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AggregateCreate(char *aggName,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *aggtransfn1Name,
|
|
|
|
char *aggtransfn2Name,
|
|
|
|
char *aggfinalfnName,
|
|
|
|
char *aggbasetypeName,
|
|
|
|
char *aggtransfn1typeName,
|
|
|
|
char *aggtransfn2typeName,
|
|
|
|
char *agginitval1,
|
|
|
|
char *agginitval2)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-02-11 20:14:04 +01:00
|
|
|
int i;
|
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;
|
|
|
|
Oid xfn1 = InvalidOid;
|
|
|
|
Oid xfn2 = InvalidOid;
|
|
|
|
Oid ffn = InvalidOid;
|
|
|
|
Oid xbase = InvalidOid;
|
|
|
|
Oid xret1 = InvalidOid;
|
|
|
|
Oid xret2 = InvalidOid;
|
|
|
|
Oid fret = InvalidOid;
|
|
|
|
Oid fnArgs[8];
|
1998-09-01 06:40:42 +02:00
|
|
|
NameData aname;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(fnArgs, 0, 8 * 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
|
|
|
|
|
|
|
if (!aggtransfn1Name && !aggtransfn2Name)
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
tup = SearchSysCacheTuple(TYPNAME,
|
|
|
|
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;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (aggtransfn1Name)
|
|
|
|
{
|
|
|
|
tup = SearchSysCacheTuple(TYPNAME,
|
|
|
|
PointerGetDatum(aggtransfn1typeName),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggtransfn1typeName);
|
1998-11-27 20:52:36 +01:00
|
|
|
xret1 = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
fnArgs[0] = xret1;
|
|
|
|
fnArgs[1] = xbase;
|
|
|
|
tup = SearchSysCacheTuple(PRONAME,
|
|
|
|
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",
|
1997-09-07 07:04:48 +02:00
|
|
|
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'",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggtransfn1Name,
|
|
|
|
aggtransfn1typeName);
|
1998-11-27 20:52:36 +01:00
|
|
|
xfn1 = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
|
|
|
|
!OidIsValid(xbase))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aggtransfn2Name)
|
|
|
|
{
|
|
|
|
tup = SearchSysCacheTuple(TYPNAME,
|
|
|
|
PointerGetDatum(aggtransfn2typeName),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggtransfn2typeName);
|
1998-11-27 20:52:36 +01:00
|
|
|
xret2 = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
fnArgs[0] = xret2;
|
|
|
|
fnArgs[1] = 0;
|
|
|
|
tup = SearchSysCacheTuple(PRONAME,
|
|
|
|
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",
|
1997-09-07 07:04:48 +02:00
|
|
|
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'",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggtransfn2Name, aggtransfn2typeName);
|
1998-11-27 20:52:36 +01:00
|
|
|
xfn2 = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!OidIsValid(xfn2) || !OidIsValid(xret2))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
tup = SearchSysCacheTuple(AGGNAME,
|
|
|
|
PointerGetDatum(aggName),
|
1997-09-07 07:04:48 +02:00
|
|
|
ObjectIdGetDatum(xbase),
|
|
|
|
0, 0);
|
|
|
|
if (HeapTupleIsValid(tup))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR,
|
1997-09-07 07:04:48 +02:00
|
|
|
"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");
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: Aggregate cannot have final function without both transition functions");
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (aggfinalfnName)
|
|
|
|
{
|
|
|
|
fnArgs[0] = xret1;
|
|
|
|
fnArgs[1] = xret2;
|
|
|
|
tup = SearchSysCacheTuple(PRONAME,
|
|
|
|
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",
|
1997-09-07 07:04:48 +02:00
|
|
|
aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
|
1998-11-27 20:52:36 +01:00
|
|
|
ffn = tup->t_data->t_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
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);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* 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");
|
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);
|
1997-09-07 07:04:48 +02:00
|
|
|
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';
|
|
|
|
|
|
|
|
if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggregateCreate: could not open '%s'",
|
1997-09-07 07:04:48 +02:00
|
|
|
AggregateRelationName);
|
|
|
|
|
|
|
|
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");
|
1997-09-07 07:04:48 +02:00
|
|
|
heap_close(aggdesc);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
char *
|
1997-09-08 23:56:23 +02:00
|
|
|
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
Relation aggRel;
|
|
|
|
int initValAttno;
|
|
|
|
Oid transtype;
|
|
|
|
text *textInitVal;
|
|
|
|
char *strInitVal,
|
|
|
|
*initVal;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(PointerIsValid(aggName));
|
|
|
|
Assert(PointerIsValid(isNull));
|
|
|
|
Assert(xfuncno == 1 || xfuncno == 2);
|
|
|
|
|
|
|
|
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);
|
|
|
|
if (xfuncno == 1)
|
|
|
|
{
|
|
|
|
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
|
|
|
|
initValAttno = Anum_pg_aggregate_agginitval1;
|
|
|
|
}
|
|
|
|
else
|
1997-09-08 04:41:22 +02:00
|
|
|
/* can only be 1 or 2 */
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
|
|
|
|
initValAttno = Anum_pg_aggregate_agginitval2;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
aggRel = heap_openr(AggregateRelationName);
|
|
|
|
if (!RelationIsValid(aggRel))
|
1998-01-06 20:42:33 +01:00
|
|
|
elog(ERROR, "AggNameGetInitVal: could not open \"%-.*s\"",
|
1997-09-07 07:04:48 +02:00
|
|
|
AggregateRelationName);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* must use fastgetattr in case one or other of the init values is
|
|
|
|
* NULL
|
|
|
|
*/
|
|
|
|
textInitVal = (text *) fastgetattr(tup, initValAttno,
|
1998-09-01 05:29:17 +02:00
|
|
|
RelationGetDescr(aggRel),
|
1997-09-07 07:04:48 +02:00
|
|
|
isNull);
|
|
|
|
if (!PointerIsValid(textInitVal))
|
|
|
|
*isNull = true;
|
|
|
|
if (*isNull)
|
|
|
|
{
|
|
|
|
heap_close(aggRel);
|
1998-09-01 05:29:17 +02:00
|
|
|
return (char *) NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
strInitVal = textout(textInitVal);
|
1996-07-09 08:22:35 +02:00
|
|
|
heap_close(aggRel);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
tup = SearchSysCacheTuple(TYPOID,
|
|
|
|
ObjectIdGetDatum(transtype),
|
1997-09-07 07:04:48 +02:00
|
|
|
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");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
initVal = fmgr(((Form_pg_type) GETSTRUCT(tup))->typinput, strInitVal, -1);
|
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
|
|
|
}
|