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.25 1999/09/18 19:06:33 tgl Exp $
12 *-------------------------------------------------------------------------
16 #include "access/heapam.h"
17 #include "catalog/catname.h"
18 #include "catalog/pg_aggregate.h"
19 #include "catalog/pg_proc.h"
20 #include "catalog/pg_type.h"
21 #include "miscadmin.h"
22 #include "utils/builtins.h"
23 #include "utils/syscache.h"
28 * aggregates overloading has been added. Instead of the full
29 * overload support we have for functions, aggregate overloading only
30 * applies to exact basetype matches. That is, we don't check the
31 * the inheritance hierarchy
34 * Currently, redefining aggregates using the same name is not
35 * supported. In such a case, a warning is printed that the
36 * aggregate already exists. If such is not the case, a new tuple
37 * is created and inserted in the aggregate relation. The fields
38 * of this tuple are aggregate name, owner id, 2 transition functions
39 * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
40 * type of data on which aggtransfn1 operates (aggbasetype), return
41 * types of the two transition functions (aggtranstype1 and
42 * aggtranstype2), final return type (aggfinaltype), and initial values
43 * for the two state transition functions (agginitval1 and agginitval2).
44 * All types and functions must have been defined
45 * prior to defining the aggregate.
50 AggregateCreate(char *aggName,
51 char *aggtransfn1Name,
52 char *aggtransfn2Name,
54 char *aggbasetypeName,
55 char *aggtransfn1typeName,
56 char *aggtransfn2typeName,
63 char nulls[Natts_pg_aggregate];
64 Datum values[Natts_pg_aggregate];
66 Oid xfn1 = InvalidOid;
67 Oid xfn2 = InvalidOid;
69 Oid xbase = InvalidOid;
70 Oid xret1 = InvalidOid;
71 Oid xret2 = InvalidOid;
72 Oid fret = InvalidOid;
77 MemSet(fnArgs, 0, 8 * sizeof(Oid));
81 elog(ERROR, "AggregateCreate: no aggregate name supplied");
83 if (!aggtransfn1Name && !aggtransfn2Name)
84 elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
86 tup = SearchSysCacheTuple(TYPNAME,
87 PointerGetDatum(aggbasetypeName),
89 if (!HeapTupleIsValid(tup))
90 elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
91 xbase = tup->t_data->t_oid;
95 tup = SearchSysCacheTuple(TYPNAME,
96 PointerGetDatum(aggtransfn1typeName),
98 if (!HeapTupleIsValid(tup))
99 elog(ERROR, "AggregateCreate: Type '%s' undefined",
100 aggtransfn1typeName);
101 xret1 = tup->t_data->t_oid;
105 tup = SearchSysCacheTuple(PRONAME,
106 PointerGetDatum(aggtransfn1Name),
108 PointerGetDatum(fnArgs),
110 if (!HeapTupleIsValid(tup))
111 elog(ERROR, "AggregateCreate: '%s('%s', '%s') does not exist",
112 aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
113 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
114 elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
116 aggtransfn1typeName);
117 xfn1 = tup->t_data->t_oid;
118 if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
120 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
125 tup = SearchSysCacheTuple(TYPNAME,
126 PointerGetDatum(aggtransfn2typeName),
128 if (!HeapTupleIsValid(tup))
129 elog(ERROR, "AggregateCreate: Type '%s' undefined",
130 aggtransfn2typeName);
131 xret2 = tup->t_data->t_oid;
135 tup = SearchSysCacheTuple(PRONAME,
136 PointerGetDatum(aggtransfn2Name),
138 PointerGetDatum(fnArgs),
140 if (!HeapTupleIsValid(tup))
141 elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
142 aggtransfn2Name, aggtransfn2typeName);
143 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
144 elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
145 aggtransfn2Name, aggtransfn2typeName);
146 xfn2 = tup->t_data->t_oid;
147 if (!OidIsValid(xfn2) || !OidIsValid(xret2))
148 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
151 tup = SearchSysCacheTuple(AGGNAME,
152 PointerGetDatum(aggName),
153 ObjectIdGetDatum(xbase),
155 if (HeapTupleIsValid(tup))
157 "AggregateCreate: aggregate '%s' with base type '%s' already exists",
158 aggName, aggbasetypeName);
160 /* more sanity checks */
161 if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
162 elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
164 if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
165 elog(ERROR, "AggregateCreate: Aggregate cannot have final function without both transition functions");
171 tup = SearchSysCacheTuple(PRONAME,
172 PointerGetDatum(aggfinalfnName),
174 PointerGetDatum(fnArgs),
176 if (!HeapTupleIsValid(tup))
177 elog(ERROR, "AggregateCreate: '%s'('%s','%s') does not exist",
178 aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
179 ffn = tup->t_data->t_oid;
180 proc = (Form_pg_proc) GETSTRUCT(tup);
181 fret = proc->prorettype;
182 if (!OidIsValid(ffn) || !OidIsValid(fret))
183 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
187 * If transition function 2 is defined, it must have an initial value,
188 * whereas transition function 1 does not, which allows man and min
189 * aggregates to return NULL if they are evaluated on empty sets.
191 if (OidIsValid(xfn2) && !agginitval2)
192 elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value");
194 /* initialize nulls and values */
195 for (i = 0; i < Natts_pg_aggregate; i++)
198 values[i] = (Datum) NULL;
200 namestrcpy(&aname, aggName);
201 values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
202 values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
203 values[Anum_pg_aggregate_aggtransfn1 - 1] = ObjectIdGetDatum(xfn1);
204 values[Anum_pg_aggregate_aggtransfn2 - 1] = ObjectIdGetDatum(xfn2);
205 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(ffn);
207 values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase);
208 if (!OidIsValid(xfn1))
210 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(InvalidOid);
211 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
212 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret2);
214 else if (!OidIsValid(xfn2))
216 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
217 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(InvalidOid);
218 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret1);
222 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
223 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
224 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(fret);
228 values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
230 nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
233 values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
235 nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
237 aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
238 tupDesc = aggdesc->rd_att;
239 if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
242 elog(ERROR, "AggregateCreate: heap_formtuple failed");
243 if (!OidIsValid(heap_insert(aggdesc, tup)))
244 elog(ERROR, "AggregateCreate: heap_insert failed");
245 heap_close(aggdesc, RowExclusiveLock);
249 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
259 Assert(PointerIsValid(aggName));
260 Assert(PointerIsValid(isNull));
261 Assert(xfuncno == 1 || xfuncno == 2);
264 * since we will have to use fastgetattr (in case one or both init vals
265 * are NULL), we will need to open the relation. Do that first to
266 * ensure we don't get a stale tuple from the cache.
269 aggRel = heap_openr(AggregateRelationName, AccessShareLock);
271 tup = SearchSysCacheTuple(AGGNAME,
272 PointerGetDatum(aggName),
273 ObjectIdGetDatum(basetype),
275 if (!HeapTupleIsValid(tup))
276 elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
280 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
281 initValAttno = Anum_pg_aggregate_agginitval1;
285 /* can only be 1 or 2 */
286 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
287 initValAttno = Anum_pg_aggregate_agginitval2;
290 textInitVal = (text *) fastgetattr(tup, initValAttno,
291 RelationGetDescr(aggRel),
293 if (!PointerIsValid(textInitVal))
297 heap_close(aggRel, AccessShareLock);
298 return (char *) NULL;
300 strInitVal = textout(textInitVal);
302 heap_close(aggRel, AccessShareLock);
304 tup = SearchSysCacheTuple(TYPOID,
305 ObjectIdGetDatum(transtype),
307 if (!HeapTupleIsValid(tup))
310 elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
312 initVal = fmgr(((Form_pg_type) GETSTRUCT(tup))->typinput, strInitVal, -1);