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.22 1999/07/15 23:03:04 momjian Exp $
12 *-------------------------------------------------------------------------
16 #include "access/heapam.h"
17 #include "utils/builtins.h"
18 #include "catalog/catname.h"
19 #include "utils/syscache.h"
20 #include "catalog/pg_proc.h"
21 #include "catalog/pg_type.h"
22 #include "catalog/pg_aggregate.h"
23 #include "miscadmin.h"
32 * aggregates overloading has been added. Instead of the full
33 * overload support we have for functions, aggregate overloading only
34 * applies to exact basetype matches. That is, we don't check the
35 * the inheritance hierarchy
38 * Currently, redefining aggregates using the same name is not
39 * supported. In such a case, a warning is printed that the
40 * aggregate already exists. If such is not the case, a new tuple
41 * is created and inserted in the aggregate relation. The fields
42 * of this tuple are aggregate name, owner id, 2 transition functions
43 * (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
44 * type of data on which aggtransfn1 operates (aggbasetype), return
45 * types of the two transition functions (aggtranstype1 and
46 * aggtranstype2), final return type (aggfinaltype), and initial values
47 * for the two state transition functions (agginitval1 and agginitval2).
48 * All types and functions must have been defined
49 * prior to defining the aggregate.
54 AggregateCreate(char *aggName,
55 char *aggtransfn1Name,
56 char *aggtransfn2Name,
58 char *aggbasetypeName,
59 char *aggtransfn1typeName,
60 char *aggtransfn2typeName,
67 char nulls[Natts_pg_aggregate];
68 Datum values[Natts_pg_aggregate];
70 Oid xfn1 = InvalidOid;
71 Oid xfn2 = InvalidOid;
73 Oid xbase = InvalidOid;
74 Oid xret1 = InvalidOid;
75 Oid xret2 = InvalidOid;
76 Oid fret = InvalidOid;
81 MemSet(fnArgs, 0, 8 * sizeof(Oid));
85 elog(ERROR, "AggregateCreate: no aggregate name supplied");
87 if (!aggtransfn1Name && !aggtransfn2Name)
88 elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
90 tup = SearchSysCacheTuple(TYPNAME,
91 PointerGetDatum(aggbasetypeName),
93 if (!HeapTupleIsValid(tup))
94 elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
95 xbase = tup->t_data->t_oid;
99 tup = SearchSysCacheTuple(TYPNAME,
100 PointerGetDatum(aggtransfn1typeName),
102 if (!HeapTupleIsValid(tup))
103 elog(ERROR, "AggregateCreate: Type '%s' undefined",
104 aggtransfn1typeName);
105 xret1 = tup->t_data->t_oid;
109 tup = SearchSysCacheTuple(PRONAME,
110 PointerGetDatum(aggtransfn1Name),
112 PointerGetDatum(fnArgs),
114 if (!HeapTupleIsValid(tup))
115 elog(ERROR, "AggregateCreate: '%s('%s', '%s') does not exist",
116 aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
117 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
118 elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
120 aggtransfn1typeName);
121 xfn1 = tup->t_data->t_oid;
122 if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
124 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
129 tup = SearchSysCacheTuple(TYPNAME,
130 PointerGetDatum(aggtransfn2typeName),
132 if (!HeapTupleIsValid(tup))
133 elog(ERROR, "AggregateCreate: Type '%s' undefined",
134 aggtransfn2typeName);
135 xret2 = tup->t_data->t_oid;
139 tup = SearchSysCacheTuple(PRONAME,
140 PointerGetDatum(aggtransfn2Name),
142 PointerGetDatum(fnArgs),
144 if (!HeapTupleIsValid(tup))
145 elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
146 aggtransfn2Name, aggtransfn2typeName);
147 if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
148 elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
149 aggtransfn2Name, aggtransfn2typeName);
150 xfn2 = tup->t_data->t_oid;
151 if (!OidIsValid(xfn2) || !OidIsValid(xret2))
152 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
155 tup = SearchSysCacheTuple(AGGNAME,
156 PointerGetDatum(aggName),
157 ObjectIdGetDatum(xbase),
159 if (HeapTupleIsValid(tup))
161 "AggregateCreate: aggregate '%s' with base type '%s' already exists",
162 aggName, aggbasetypeName);
164 /* more sanity checks */
165 if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
166 elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
168 if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
169 elog(ERROR, "AggregateCreate: Aggregate cannot have final function without both transition functions");
175 tup = SearchSysCacheTuple(PRONAME,
176 PointerGetDatum(aggfinalfnName),
178 PointerGetDatum(fnArgs),
180 if (!HeapTupleIsValid(tup))
181 elog(ERROR, "AggregateCreate: '%s'('%s','%s') does not exist",
182 aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
183 ffn = tup->t_data->t_oid;
184 proc = (Form_pg_proc) GETSTRUCT(tup);
185 fret = proc->prorettype;
186 if (!OidIsValid(ffn) || !OidIsValid(fret))
187 elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
191 * If transition function 2 is defined, it must have an initial value,
192 * whereas transition function 1 does not, which allows man and min
193 * aggregates to return NULL if they are evaluated on empty sets.
195 if (OidIsValid(xfn2) && !agginitval2)
196 elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value");
198 /* initialize nulls and values */
199 for (i = 0; i < Natts_pg_aggregate; i++)
202 values[i] = (Datum) NULL;
204 namestrcpy(&aname, aggName);
205 values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
206 values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
207 values[Anum_pg_aggregate_aggtransfn1 - 1] = ObjectIdGetDatum(xfn1);
208 values[Anum_pg_aggregate_aggtransfn2 - 1] = ObjectIdGetDatum(xfn2);
209 values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(ffn);
211 values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase);
212 if (!OidIsValid(xfn1))
214 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(InvalidOid);
215 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
216 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret2);
218 else if (!OidIsValid(xfn2))
220 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
221 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(InvalidOid);
222 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(xret1);
226 values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
227 values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
228 values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(fret);
232 values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
234 nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
237 values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
239 nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
241 if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
242 elog(ERROR, "AggregateCreate: could not open '%s'",
243 AggregateRelationName);
245 tupDesc = aggdesc->rd_att;
246 if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
249 elog(ERROR, "AggregateCreate: heap_formtuple failed");
250 if (!OidIsValid(heap_insert(aggdesc, tup)))
251 elog(ERROR, "AggregateCreate: heap_insert failed");
257 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
267 Assert(PointerIsValid(aggName));
268 Assert(PointerIsValid(isNull));
269 Assert(xfuncno == 1 || xfuncno == 2);
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;
284 /* can only be 1 or 2 */
286 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
287 initValAttno = Anum_pg_aggregate_agginitval2;
290 aggRel = heap_openr(AggregateRelationName);
291 if (!RelationIsValid(aggRel))
292 elog(ERROR, "AggNameGetInitVal: could not open \"%-.*s\"",
293 AggregateRelationName);
296 * must use fastgetattr in case one or other of the init values is
299 textInitVal = (text *) fastgetattr(tup, initValAttno,
300 RelationGetDescr(aggRel),
302 if (!PointerIsValid(textInitVal))
307 return (char *) NULL;
309 strInitVal = textout(textInitVal);
312 tup = SearchSysCacheTuple(TYPOID,
313 ObjectIdGetDatum(transtype),
315 if (!HeapTupleIsValid(tup))
318 elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
320 initVal = fmgr(((Form_pg_type) GETSTRUCT(tup))->typinput, strInitVal, -1);