1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_aggregate relation
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $
12 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "access/relscan.h"
19 #include "access/skey.h"
20 #include "access/htup.h"
21 #include "access/tupdesc.h"
22 #include "utils/rel.h"
23 #include "utils/elog.h"
24 #include "utils/palloc.h"
25 #include "utils/builtins.h"
28 #include "catalog/catname.h"
29 #include "utils/syscache.h"
30 #include "catalog/pg_operator.h"
31 #include "catalog/pg_proc.h"
32 #include "catalog/pg_type.h"
33 #include "catalog/pg_aggregate.h"
38 * aggregates overloading has been added. Instead of the full
39 * overload support we have for functions, aggregate overloading only
40 * applies to exact basetype matches. That is, we don't check the
41 * the inheritance hierarchy
44 * Currently, redefining aggregates using the same name is not
45 * supported. In such a case, a warning is printed that the
46 * aggregate already exists. If such is not the case, a new tuple
47 * is created and inserted in the aggregate relation. The fields
48 * of this tuple are aggregate name, owner id, 2 transition functions
49 * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
50 * type of data on which aggtransfn1 operates (aggbasetype), return
51 * types of the two transition functions (aggtranstype1 and
52 * aggtranstype2), final return type (aggfinaltype), and initial values
53 * for the two state transition functions (agginitval1 and agginitval2).
54 * All types and functions must have been defined
55 * prior to defining the aggregate.
60 AggregateCreate(char *aggName,
61 char *aggtransfn1Name,
62 char *aggtransfn2Name,
64 char *aggbasetypeName,
65 char *aggtransfn1typeName,
66 char *aggtransfn2typeName,
73 char nulls[Natts_pg_aggregate];
74 Datum values[Natts_pg_aggregate];
76 Oid xfn1 = InvalidOid;
77 Oid xfn2 = InvalidOid;
79 Oid xbase = InvalidOid;
80 Oid xret1 = InvalidOid;
81 Oid xret2 = InvalidOid;
82 Oid fret = InvalidOid;
86 memset(fnArgs, 0, 8 * sizeof(Oid));
90 elog(WARN, "AggregateCreate: no aggregate name supplied");
92 if (!aggtransfn1Name && !aggtransfn2Name)
93 elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
95 tup = SearchSysCacheTuple(TYPNAME,
96 PointerGetDatum(aggbasetypeName),
98 if(!HeapTupleIsValid(tup))
99 elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
102 if (aggtransfn1Name) {
103 tup = SearchSysCacheTuple(TYPNAME,
104 PointerGetDatum(aggtransfn1typeName),
106 if(!HeapTupleIsValid(tup))
107 elog(WARN, "AggregateCreate: Type '%s' undefined",
108 aggtransfn1typeName);
113 tup = SearchSysCacheTuple(PRONAME,
114 PointerGetDatum(aggtransfn1Name),
116 PointerGetDatum(fnArgs),
118 if(!HeapTupleIsValid(tup))
119 elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist",
120 aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
121 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
122 elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
124 aggtransfn1typeName);
126 if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
128 elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
131 if (aggtransfn2Name) {
132 tup = SearchSysCacheTuple(TYPNAME,
133 PointerGetDatum(aggtransfn2typeName),
135 if(!HeapTupleIsValid(tup))
136 elog(WARN, "AggregateCreate: Type '%s' undefined",
137 aggtransfn2typeName);
142 tup = SearchSysCacheTuple(PRONAME,
143 PointerGetDatum(aggtransfn2Name),
145 PointerGetDatum(fnArgs),
147 if(!HeapTupleIsValid(tup))
148 elog(WARN, "AggregateCreate: '%s'('%s') does not exist",
149 aggtransfn2Name, aggtransfn2typeName);
150 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
151 elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
152 aggtransfn2Name, aggtransfn2typeName);
154 if (!OidIsValid(xfn2) || !OidIsValid(xret2))
155 elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
158 tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
159 ObjectIdGetDatum(xbase),
161 if (HeapTupleIsValid(tup))
163 "AggregateCreate: aggregate '%s' with base type '%s' already exists",
164 aggName, aggbasetypeName);
166 /* more sanity checks */
167 if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
168 elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
170 if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
171 elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
173 if (aggfinalfnName) {
176 tup = SearchSysCacheTuple(PRONAME,
177 PointerGetDatum(aggfinalfnName),
179 PointerGetDatum(fnArgs),
181 if(!HeapTupleIsValid(tup))
182 elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
183 aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
185 proc = (Form_pg_proc) GETSTRUCT(tup);
186 fret = proc->prorettype;
187 if (!OidIsValid(ffn) || !OidIsValid(fret))
188 elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
192 * If transition function 2 is defined, it must have an initial value,
193 * whereas transition function 1 does not, which allows man and min
194 * aggregates to return NULL if they are evaluated on empty sets.
196 if (OidIsValid(xfn2) && !agginitval2)
197 elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
199 /* initialize nulls and values */
200 for(i=0; i < Natts_pg_aggregate; i++) {
202 values[i] = (Datum)NULL;
204 values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName);
205 values[Anum_pg_aggregate_aggowner-1] =
206 Int32GetDatum(GetUserId());
207 values[Anum_pg_aggregate_aggtransfn1-1] =
208 ObjectIdGetDatum(xfn1);
209 values[Anum_pg_aggregate_aggtransfn2-1] =
210 ObjectIdGetDatum(xfn2);
211 values[Anum_pg_aggregate_aggfinalfn-1] =
212 ObjectIdGetDatum(ffn);
214 values[Anum_pg_aggregate_aggbasetype-1] =
215 ObjectIdGetDatum(xbase);
216 if (!OidIsValid(xfn1)) {
217 values[Anum_pg_aggregate_aggtranstype1-1] =
218 ObjectIdGetDatum(InvalidOid);
219 values[Anum_pg_aggregate_aggtranstype2-1] =
220 ObjectIdGetDatum(xret2);
221 values[Anum_pg_aggregate_aggfinaltype-1] =
222 ObjectIdGetDatum(xret2);
224 else if (!OidIsValid(xfn2)) {
225 values[Anum_pg_aggregate_aggtranstype1-1] =
226 ObjectIdGetDatum(xret1);
227 values[Anum_pg_aggregate_aggtranstype2-1] =
228 ObjectIdGetDatum(InvalidOid);
229 values[Anum_pg_aggregate_aggfinaltype-1] =
230 ObjectIdGetDatum(xret1);
233 values[Anum_pg_aggregate_aggtranstype1-1] =
234 ObjectIdGetDatum(xret1);
235 values[Anum_pg_aggregate_aggtranstype2-1] =
236 ObjectIdGetDatum(xret2);
237 values[Anum_pg_aggregate_aggfinaltype-1] =
238 ObjectIdGetDatum(fret);
242 values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
244 nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
247 values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
249 nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
251 if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
252 elog(WARN, "AggregateCreate: could not open '%s'",
253 AggregateRelationName);
255 tupDesc = aggdesc->rd_att;
256 if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
259 elog(WARN, "AggregateCreate: heap_formtuple failed");
260 if (!OidIsValid(heap_insert(aggdesc, tup)))
261 elog(WARN, "AggregateCreate: heap_insert failed");
267 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
274 char *strInitVal, *initVal;
275 extern char *textout();
277 Assert(PointerIsValid(aggName));
278 Assert(PointerIsValid(isNull));
279 Assert(xfuncno == 1 || xfuncno == 2);
281 tup = SearchSysCacheTuple(AGGNAME,
282 PointerGetDatum(aggName),
283 PointerGetDatum(basetype),
285 if (!HeapTupleIsValid(tup))
286 elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
289 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
290 initValAttno = Anum_pg_aggregate_agginitval1;
292 else if (xfuncno == 2) {
293 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
294 initValAttno = Anum_pg_aggregate_agginitval2;
297 aggRel = heap_openr(AggregateRelationName);
298 if (!RelationIsValid(aggRel))
299 elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
300 AggregateRelationName);
302 * must use fastgetattr in case one or other of the init values is NULL
304 textInitVal = (text *) fastgetattr(tup, initValAttno,
305 RelationGetTupleDescriptor(aggRel),
307 if (!PointerIsValid(textInitVal))
311 return((char *) NULL);
313 strInitVal = textout(textInitVal);
316 tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
318 if (!HeapTupleIsValid(tup)) {
320 elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
322 initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);