]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_aggregate.c
Mega-commit to make heap_open/heap_openr/heap_close take an
[postgresql] / src / backend / catalog / pg_aggregate.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_aggregate.c
4  *        routines to support manipulation of the pg_aggregate relation
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.25 1999/09/18 19:06:33 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
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"
24
25 /* ----------------
26  * AggregateCreate
27  *
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
32  *
33  * OLD COMMENTS:
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.
46  *
47  * ---------------
48  */
49 void
50 AggregateCreate(char *aggName,
51                                 char *aggtransfn1Name,
52                                 char *aggtransfn2Name,
53                                 char *aggfinalfnName,
54                                 char *aggbasetypeName,
55                                 char *aggtransfn1typeName,
56                                 char *aggtransfn2typeName,
57                                 char *agginitval1,
58                                 char *agginitval2)
59 {
60         int                     i;
61         Relation        aggdesc;
62         HeapTuple       tup;
63         char            nulls[Natts_pg_aggregate];
64         Datum           values[Natts_pg_aggregate];
65         Form_pg_proc proc;
66         Oid                     xfn1 = InvalidOid;
67         Oid                     xfn2 = InvalidOid;
68         Oid                     ffn = InvalidOid;
69         Oid                     xbase = InvalidOid;
70         Oid                     xret1 = InvalidOid;
71         Oid                     xret2 = InvalidOid;
72         Oid                     fret = InvalidOid;
73         Oid                     fnArgs[8];
74         NameData        aname;
75         TupleDesc       tupDesc;
76
77         MemSet(fnArgs, 0, 8 * sizeof(Oid));
78
79         /* sanity checks */
80         if (!aggName)
81                 elog(ERROR, "AggregateCreate: no aggregate name supplied");
82
83         if (!aggtransfn1Name && !aggtransfn2Name)
84                 elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
85
86         tup = SearchSysCacheTuple(TYPNAME,
87                                                           PointerGetDatum(aggbasetypeName),
88                                                           0, 0, 0);
89         if (!HeapTupleIsValid(tup))
90                 elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
91         xbase = tup->t_data->t_oid;
92
93         if (aggtransfn1Name)
94         {
95                 tup = SearchSysCacheTuple(TYPNAME,
96                                                                   PointerGetDatum(aggtransfn1typeName),
97                                                                   0, 0, 0);
98                 if (!HeapTupleIsValid(tup))
99                         elog(ERROR, "AggregateCreate: Type '%s' undefined",
100                                  aggtransfn1typeName);
101                 xret1 = tup->t_data->t_oid;
102
103                 fnArgs[0] = xret1;
104                 fnArgs[1] = xbase;
105                 tup = SearchSysCacheTuple(PRONAME,
106                                                                   PointerGetDatum(aggtransfn1Name),
107                                                                   Int32GetDatum(2),
108                                                                   PointerGetDatum(fnArgs),
109                                                                   0);
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'",
115                                  aggtransfn1Name,
116                                  aggtransfn1typeName);
117                 xfn1 = tup->t_data->t_oid;
118                 if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
119                         !OidIsValid(xbase))
120                         elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
121         }
122
123         if (aggtransfn2Name)
124         {
125                 tup = SearchSysCacheTuple(TYPNAME,
126                                                                   PointerGetDatum(aggtransfn2typeName),
127                                                                   0, 0, 0);
128                 if (!HeapTupleIsValid(tup))
129                         elog(ERROR, "AggregateCreate: Type '%s' undefined",
130                                  aggtransfn2typeName);
131                 xret2 = tup->t_data->t_oid;
132
133                 fnArgs[0] = xret2;
134                 fnArgs[1] = 0;
135                 tup = SearchSysCacheTuple(PRONAME,
136                                                                   PointerGetDatum(aggtransfn2Name),
137                                                                   Int32GetDatum(1),
138                                                                   PointerGetDatum(fnArgs),
139                                                                   0);
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);
149         }
150
151         tup = SearchSysCacheTuple(AGGNAME,
152                                                           PointerGetDatum(aggName),
153                                                           ObjectIdGetDatum(xbase),
154                                                           0, 0);
155         if (HeapTupleIsValid(tup))
156                 elog(ERROR,
157                          "AggregateCreate: aggregate '%s' with base type '%s' already exists",
158                          aggName, aggbasetypeName);
159
160         /* more sanity checks */
161         if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
162                 elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
163
164         if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
165                 elog(ERROR, "AggregateCreate: Aggregate cannot have final function without both transition functions");
166
167         if (aggfinalfnName)
168         {
169                 fnArgs[0] = xret1;
170                 fnArgs[1] = xret2;
171                 tup = SearchSysCacheTuple(PRONAME,
172                                                                   PointerGetDatum(aggfinalfnName),
173                                                                   Int32GetDatum(2),
174                                                                   PointerGetDatum(fnArgs),
175                                                                   0);
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);
184         }
185
186         /*
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.
190          */
191         if (OidIsValid(xfn2) && !agginitval2)
192                 elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value");
193
194         /* initialize nulls and values */
195         for (i = 0; i < Natts_pg_aggregate; i++)
196         {
197                 nulls[i] = ' ';
198                 values[i] = (Datum) NULL;
199         }
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);
206
207         values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase);
208         if (!OidIsValid(xfn1))
209         {
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);
213         }
214         else if (!OidIsValid(xfn2))
215         {
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);
219         }
220         else
221         {
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);
225         }
226
227         if (agginitval1)
228                 values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
229         else
230                 nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
231
232         if (agginitval2)
233                 values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
234         else
235                 nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
236
237         aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
238         tupDesc = aggdesc->rd_att;
239         if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
240                                                                                            values,
241                                                                                            nulls)))
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);
246 }
247
248 char *
249 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
250 {
251         HeapTuple       tup;
252         Relation        aggRel;
253         int                     initValAttno;
254         Oid                     transtype;
255         text       *textInitVal;
256         char       *strInitVal,
257                            *initVal;
258
259         Assert(PointerIsValid(aggName));
260         Assert(PointerIsValid(isNull));
261         Assert(xfuncno == 1 || xfuncno == 2);
262
263         /*
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.
267          */
268
269         aggRel = heap_openr(AggregateRelationName, AccessShareLock);
270
271         tup = SearchSysCacheTuple(AGGNAME,
272                                                           PointerGetDatum(aggName),
273                                                           ObjectIdGetDatum(basetype),
274                                                           0, 0);
275         if (!HeapTupleIsValid(tup))
276                 elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
277                          aggName);
278         if (xfuncno == 1)
279         {
280                 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
281                 initValAttno = Anum_pg_aggregate_agginitval1;
282         }
283         else
284         {
285                 /* can only be 1 or 2 */
286                 transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
287                 initValAttno = Anum_pg_aggregate_agginitval2;
288         }
289
290         textInitVal = (text *) fastgetattr(tup, initValAttno,
291                                                                            RelationGetDescr(aggRel),
292                                                                            isNull);
293         if (!PointerIsValid(textInitVal))
294                 *isNull = true;
295         if (*isNull)
296         {
297                 heap_close(aggRel, AccessShareLock);
298                 return (char *) NULL;
299         }
300         strInitVal = textout(textInitVal);
301
302         heap_close(aggRel, AccessShareLock);
303
304         tup = SearchSysCacheTuple(TYPOID,
305                                                           ObjectIdGetDatum(transtype),
306                                                           0, 0, 0);
307         if (!HeapTupleIsValid(tup))
308         {
309                 pfree(strInitVal);
310                 elog(ERROR, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
311         }
312         initVal = fmgr(((Form_pg_type) GETSTRUCT(tup))->typinput, strInitVal, -1);
313         pfree(strInitVal);
314         return initVal;
315 }