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.3 1996/11/06 07:31:23 scrappy Exp $
12 *-------------------------------------------------------------------------
16 #include <access/heapam.h>
17 #include <utils/builtins.h>
19 #include <catalog/catname.h>
20 #include <utils/syscache.h>
21 #include <catalog/pg_operator.h>
22 #include <catalog/pg_proc.h>
23 #include <catalog/pg_type.h>
24 #include <catalog/pg_aggregate.h>
25 #include <miscadmin.h>
27 # include <regex/utils.h>
35 * aggregates overloading has been added. Instead of the full
36 * overload support we have for functions, aggregate overloading only
37 * applies to exact basetype matches. That is, we don't check the
38 * the inheritance hierarchy
41 * Currently, redefining aggregates using the same name is not
42 * supported. In such a case, a warning is printed that the
43 * aggregate already exists. If such is not the case, a new tuple
44 * is created and inserted in the aggregate relation. The fields
45 * of this tuple are aggregate name, owner id, 2 transition functions
46 * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
47 * type of data on which aggtransfn1 operates (aggbasetype), return
48 * types of the two transition functions (aggtranstype1 and
49 * aggtranstype2), final return type (aggfinaltype), and initial values
50 * for the two state transition functions (agginitval1 and agginitval2).
51 * All types and functions must have been defined
52 * prior to defining the aggregate.
57 AggregateCreate(char *aggName,
58 char *aggtransfn1Name,
59 char *aggtransfn2Name,
61 char *aggbasetypeName,
62 char *aggtransfn1typeName,
63 char *aggtransfn2typeName,
70 char nulls[Natts_pg_aggregate];
71 Datum values[Natts_pg_aggregate];
73 Oid xfn1 = InvalidOid;
74 Oid xfn2 = InvalidOid;
76 Oid xbase = InvalidOid;
77 Oid xret1 = InvalidOid;
78 Oid xret2 = InvalidOid;
79 Oid fret = InvalidOid;
83 memset(fnArgs, 0, 8 * sizeof(Oid));
87 elog(WARN, "AggregateCreate: no aggregate name supplied");
89 if (!aggtransfn1Name && !aggtransfn2Name)
90 elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
92 tup = SearchSysCacheTuple(TYPNAME,
93 PointerGetDatum(aggbasetypeName),
95 if(!HeapTupleIsValid(tup))
96 elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
99 if (aggtransfn1Name) {
100 tup = SearchSysCacheTuple(TYPNAME,
101 PointerGetDatum(aggtransfn1typeName),
103 if(!HeapTupleIsValid(tup))
104 elog(WARN, "AggregateCreate: Type '%s' undefined",
105 aggtransfn1typeName);
110 tup = SearchSysCacheTuple(PRONAME,
111 PointerGetDatum(aggtransfn1Name),
113 PointerGetDatum(fnArgs),
115 if(!HeapTupleIsValid(tup))
116 elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist",
117 aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
118 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
119 elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
121 aggtransfn1typeName);
123 if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
125 elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
128 if (aggtransfn2Name) {
129 tup = SearchSysCacheTuple(TYPNAME,
130 PointerGetDatum(aggtransfn2typeName),
132 if(!HeapTupleIsValid(tup))
133 elog(WARN, "AggregateCreate: Type '%s' undefined",
134 aggtransfn2typeName);
139 tup = SearchSysCacheTuple(PRONAME,
140 PointerGetDatum(aggtransfn2Name),
142 PointerGetDatum(fnArgs),
144 if(!HeapTupleIsValid(tup))
145 elog(WARN, "AggregateCreate: '%s'('%s') does not exist",
146 aggtransfn2Name, aggtransfn2typeName);
147 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
148 elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
149 aggtransfn2Name, aggtransfn2typeName);
151 if (!OidIsValid(xfn2) || !OidIsValid(xret2))
152 elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
155 tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
156 ObjectIdGetDatum(xbase),
158 if (HeapTupleIsValid(tup))
160 "AggregateCreate: aggregate '%s' with base type '%s' already exists",
161 aggName, aggbasetypeName);
163 /* more sanity checks */
164 if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
165 elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
167 if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
168 elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
170 if (aggfinalfnName) {
173 tup = SearchSysCacheTuple(PRONAME,
174 PointerGetDatum(aggfinalfnName),
176 PointerGetDatum(fnArgs),
178 if(!HeapTupleIsValid(tup))
179 elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
180 aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
182 proc = (Form_pg_proc) GETSTRUCT(tup);
183 fret = proc->prorettype;
184 if (!OidIsValid(ffn) || !OidIsValid(fret))
185 elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
189 * If transition function 2 is defined, it must have an initial value,
190 * whereas transition function 1 does not, which allows man and min
191 * aggregates to return NULL if they are evaluated on empty sets.
193 if (OidIsValid(xfn2) && !agginitval2)
194 elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
196 /* initialize nulls and values */
197 for(i=0; i < Natts_pg_aggregate; i++) {
199 values[i] = (Datum)NULL;
201 values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName);
202 values[Anum_pg_aggregate_aggowner-1] =
203 Int32GetDatum(GetUserId());
204 values[Anum_pg_aggregate_aggtransfn1-1] =
205 ObjectIdGetDatum(xfn1);
206 values[Anum_pg_aggregate_aggtransfn2-1] =
207 ObjectIdGetDatum(xfn2);
208 values[Anum_pg_aggregate_aggfinalfn-1] =
209 ObjectIdGetDatum(ffn);
211 values[Anum_pg_aggregate_aggbasetype-1] =
212 ObjectIdGetDatum(xbase);
213 if (!OidIsValid(xfn1)) {
214 values[Anum_pg_aggregate_aggtranstype1-1] =
215 ObjectIdGetDatum(InvalidOid);
216 values[Anum_pg_aggregate_aggtranstype2-1] =
217 ObjectIdGetDatum(xret2);
218 values[Anum_pg_aggregate_aggfinaltype-1] =
219 ObjectIdGetDatum(xret2);
221 else if (!OidIsValid(xfn2)) {
222 values[Anum_pg_aggregate_aggtranstype1-1] =
223 ObjectIdGetDatum(xret1);
224 values[Anum_pg_aggregate_aggtranstype2-1] =
225 ObjectIdGetDatum(InvalidOid);
226 values[Anum_pg_aggregate_aggfinaltype-1] =
227 ObjectIdGetDatum(xret1);
230 values[Anum_pg_aggregate_aggtranstype1-1] =
231 ObjectIdGetDatum(xret1);
232 values[Anum_pg_aggregate_aggtranstype2-1] =
233 ObjectIdGetDatum(xret2);
234 values[Anum_pg_aggregate_aggfinaltype-1] =
235 ObjectIdGetDatum(fret);
239 values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
241 nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
244 values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
246 nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
248 if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
249 elog(WARN, "AggregateCreate: could not open '%s'",
250 AggregateRelationName);
252 tupDesc = aggdesc->rd_att;
253 if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
256 elog(WARN, "AggregateCreate: heap_formtuple failed");
257 if (!OidIsValid(heap_insert(aggdesc, tup)))
258 elog(WARN, "AggregateCreate: heap_insert failed");
264 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
271 char *strInitVal, *initVal;
272 extern char *textout();
274 Assert(PointerIsValid(aggName));
275 Assert(PointerIsValid(isNull));
276 Assert(xfuncno == 1 || xfuncno == 2);
278 tup = SearchSysCacheTuple(AGGNAME,
279 PointerGetDatum(aggName),
280 PointerGetDatum(basetype),
282 if (!HeapTupleIsValid(tup))
283 elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
286 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
287 initValAttno = Anum_pg_aggregate_agginitval1;
289 else if (xfuncno == 2) {
290 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
291 initValAttno = Anum_pg_aggregate_agginitval2;
294 aggRel = heap_openr(AggregateRelationName);
295 if (!RelationIsValid(aggRel))
296 elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
297 AggregateRelationName);
299 * must use fastgetattr in case one or other of the init values is NULL
301 textInitVal = (text *) fastgetattr(tup, initValAttno,
302 RelationGetTupleDescriptor(aggRel),
304 if (!PointerIsValid(textInitVal))
308 return((char *) NULL);
310 strInitVal = textout(textInitVal);
313 tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
315 if (!HeapTupleIsValid(tup)) {
317 elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
319 initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);