]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_aggregate.c
Postgres95 1.01 Distribution - Virgin Sources
[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.1.1.1 1996/07/09 06:21:16 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <string.h>
15 #include "postgres.h"
16
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"
26 #include "fmgr.h"
27
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"
34
35 /* ----------------
36  * AggregateCreate
37  *
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
42  *
43  * OLD COMMENTS:
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.
56  * 
57  * ---------------
58  */
59 void
60 AggregateCreate(char *aggName,
61                 char *aggtransfn1Name,
62                 char *aggtransfn2Name,
63                 char *aggfinalfnName, 
64                 char *aggbasetypeName,
65                 char *aggtransfn1typeName,
66                 char *aggtransfn2typeName,
67                 char *agginitval1,
68                 char *agginitval2)
69 {
70     register            i;
71     Relation            aggdesc;
72     HeapTuple           tup;
73     char                nulls[Natts_pg_aggregate];
74     Datum               values[Natts_pg_aggregate];
75     Form_pg_proc        proc;
76     Oid         xfn1 = InvalidOid;
77     Oid         xfn2 = InvalidOid;
78     Oid         ffn = InvalidOid;
79     Oid         xbase = InvalidOid;
80     Oid         xret1 = InvalidOid;
81     Oid         xret2 = InvalidOid;
82     Oid         fret = InvalidOid;
83     Oid         fnArgs[8];
84     TupleDesc    tupDesc;
85     
86     memset(fnArgs, 0, 8 * sizeof(Oid)); 
87     
88     /* sanity checks */
89     if (!aggName)
90         elog(WARN, "AggregateCreate: no aggregate name supplied");
91     
92     if (!aggtransfn1Name && !aggtransfn2Name)
93         elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
94     
95     tup = SearchSysCacheTuple(TYPNAME, 
96                               PointerGetDatum(aggbasetypeName),
97                               0,0,0);
98     if(!HeapTupleIsValid(tup))
99         elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
100     xbase = tup->t_oid;
101
102     if (aggtransfn1Name) {
103         tup = SearchSysCacheTuple(TYPNAME, 
104                                   PointerGetDatum(aggtransfn1typeName),
105                                   0,0,0);
106         if(!HeapTupleIsValid(tup))
107             elog(WARN, "AggregateCreate: Type '%s' undefined",
108                  aggtransfn1typeName);
109         xret1 = tup->t_oid;
110         
111         fnArgs[0] = xret1;
112         fnArgs[1] = xbase;
113         tup = SearchSysCacheTuple(PRONAME,
114                                   PointerGetDatum(aggtransfn1Name),
115                                   Int32GetDatum(2),
116                                   PointerGetDatum(fnArgs),
117                                   0);
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'",
123                  aggtransfn1Name,
124                  aggtransfn1typeName);
125         xfn1 = tup->t_oid;
126         if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
127             !OidIsValid(xbase))
128             elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
129     }
130     
131     if (aggtransfn2Name) {
132         tup = SearchSysCacheTuple(TYPNAME, 
133                                   PointerGetDatum(aggtransfn2typeName),
134                                   0,0,0);
135         if(!HeapTupleIsValid(tup))
136             elog(WARN, "AggregateCreate: Type '%s' undefined",
137                  aggtransfn2typeName);
138         xret2 = tup->t_oid;
139         
140         fnArgs[0] = xret2;
141         fnArgs[1] = 0;
142         tup = SearchSysCacheTuple(PRONAME, 
143                                   PointerGetDatum(aggtransfn2Name),
144                                   Int32GetDatum(1),
145                                   PointerGetDatum(fnArgs),
146                                   0);
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);
153         xfn2 = tup->t_oid;
154         if (!OidIsValid(xfn2) || !OidIsValid(xret2))
155             elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
156     }
157     
158     tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
159                               ObjectIdGetDatum(xbase),  
160                               0,0);
161     if (HeapTupleIsValid(tup))
162         elog(WARN, 
163              "AggregateCreate: aggregate '%s' with base type '%s' already exists",
164              aggName, aggbasetypeName);
165
166     /* more sanity checks */
167     if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
168         elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
169     
170     if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
171         elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
172     
173     if (aggfinalfnName) {
174         fnArgs[0] = xret1;
175         fnArgs[1] = xret2;
176         tup = SearchSysCacheTuple(PRONAME,
177                                   PointerGetDatum(aggfinalfnName),
178                                   Int32GetDatum(2),
179                                   PointerGetDatum(fnArgs),
180                                   0);
181         if(!HeapTupleIsValid(tup))
182             elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
183                  aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
184         ffn = tup->t_oid;
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);
189     }
190     
191     /*
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.
195      */
196     if (OidIsValid(xfn2) && !agginitval2)
197         elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
198     
199     /* initialize nulls and values */
200     for(i=0; i < Natts_pg_aggregate; i++) {
201         nulls[i] = ' ';
202         values[i] = (Datum)NULL;
203     }
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);
213     
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);
223     }
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);
231     }
232     else {
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);
239     }
240     
241     if (agginitval1)
242         values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
243     else
244         nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
245     
246     if (agginitval2)
247         values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
248     else
249         nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
250     
251     if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
252         elog(WARN, "AggregateCreate: could not open '%s'",
253              AggregateRelationName);
254
255     tupDesc = aggdesc->rd_att;
256     if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
257                                                values,
258                                                nulls)))
259         elog(WARN, "AggregateCreate: heap_formtuple failed");
260     if (!OidIsValid(heap_insert(aggdesc, tup)))
261         elog(WARN, "AggregateCreate: heap_insert failed");
262     heap_close(aggdesc);
263
264 }
265
266 char *
267 AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
268 {
269     HeapTuple   tup;
270     Relation    aggRel;
271     int         initValAttno;
272     Oid transtype;
273     text        *textInitVal;
274     char        *strInitVal, *initVal;
275     extern char *textout();
276     
277     Assert(PointerIsValid(aggName));
278     Assert(PointerIsValid(isNull));
279     Assert(xfuncno == 1 || xfuncno == 2);
280
281     tup = SearchSysCacheTuple(AGGNAME, 
282                               PointerGetDatum(aggName),
283                               PointerGetDatum(basetype),
284                               0,0);
285     if (!HeapTupleIsValid(tup))
286         elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
287              aggName);
288     if (xfuncno == 1) {
289         transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
290         initValAttno = Anum_pg_aggregate_agginitval1;
291     }
292     else if (xfuncno == 2) {
293         transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
294         initValAttno = Anum_pg_aggregate_agginitval2;
295     }
296     
297     aggRel = heap_openr(AggregateRelationName);
298     if (!RelationIsValid(aggRel))
299         elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
300              AggregateRelationName);
301     /* 
302      * must use fastgetattr in case one or other of the init values is NULL
303      */
304     textInitVal = (text *) fastgetattr(tup, initValAttno, 
305                                        RelationGetTupleDescriptor(aggRel),
306                                        isNull);
307     if (!PointerIsValid(textInitVal))
308         *isNull = true;
309     if (*isNull) {
310         heap_close(aggRel);
311         return((char *) NULL);
312     }
313     strInitVal = textout(textInitVal);
314     heap_close(aggRel);
315     
316     tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
317                               0,0,0);
318     if (!HeapTupleIsValid(tup)) {
319         pfree(strInitVal);
320         elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
321     }
322     initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
323     pfree(strInitVal);
324     return(initVal);
325 }