]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_aggregate.c
Add a bunch of pseudo-types to replace the behavior formerly associated
[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  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.54 2002/08/22 00:01:41 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_aggregate.h"
23 #include "catalog/pg_language.h"
24 #include "catalog/pg_proc.h"
25 #include "optimizer/cost.h"
26 #include "parser/parse_coerce.h"
27 #include "parser/parse_func.h"
28 #include "utils/builtins.h"
29 #include "utils/syscache.h"
30
31
32 /*
33  * AggregateCreate
34  */
35 void
36 AggregateCreate(const char *aggName,
37                                 Oid aggNamespace,
38                                 List *aggtransfnName,
39                                 List *aggfinalfnName,
40                                 Oid aggBaseType,
41                                 Oid aggTransType,
42                                 const char *agginitval)
43 {
44         Relation        aggdesc;
45         HeapTuple       tup;
46         char            nulls[Natts_pg_aggregate];
47         Datum           values[Natts_pg_aggregate];
48         Form_pg_proc proc;
49         Oid                     transfn;
50         Oid                     finalfn = InvalidOid;   /* can be omitted */
51         Oid                     finaltype;
52         Oid                     fnArgs[FUNC_MAX_ARGS];
53         int                     nargs;
54         Oid                     procOid;
55         TupleDesc       tupDesc;
56         int                     i;
57         ObjectAddress   myself,
58                                         referenced;
59
60         /* sanity checks */
61         if (!aggName)
62                 elog(ERROR, "no aggregate name supplied");
63
64         if (!aggtransfnName)
65                 elog(ERROR, "aggregate must have a transition function");
66
67         /* handle transfn */
68         MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
69         fnArgs[0] = aggTransType;
70         if (aggBaseType == ANYOID)
71                 nargs = 1;
72         else
73         {
74                 fnArgs[1] = aggBaseType;
75                 nargs = 2;
76         }
77         transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
78         if (!OidIsValid(transfn))
79                 func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
80         tup = SearchSysCache(PROCOID,
81                                                  ObjectIdGetDatum(transfn),
82                                                  0, 0, 0);
83         if (!HeapTupleIsValid(tup))
84                 func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
85         proc = (Form_pg_proc) GETSTRUCT(tup);
86         if (proc->prorettype != aggTransType)
87                 elog(ERROR, "return type of transition function %s is not %s",
88                          NameListToString(aggtransfnName), format_type_be(aggTransType));
89
90         /*
91          * If the transfn is strict and the initval is NULL, make sure input
92          * type and transtype are the same (or at least binary-compatible),
93          * so that it's OK to use the first input value as the initial
94          * transValue.
95          */
96         if (proc->proisstrict && agginitval == NULL)
97         {
98                 if (!IsBinaryCompatible(aggBaseType, aggTransType))
99                         elog(ERROR, "must not omit initval when transfn is strict and transtype is not compatible with input type");
100         }
101         ReleaseSysCache(tup);
102
103         /* handle finalfn, if supplied */
104         if (aggfinalfnName)
105         {
106                 MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
107                 fnArgs[0] = aggTransType;
108                 finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
109                 if (!OidIsValid(finalfn))
110                         func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
111                 tup = SearchSysCache(PROCOID,
112                                                          ObjectIdGetDatum(finalfn),
113                                                          0, 0, 0);
114                 if (!HeapTupleIsValid(tup))
115                         func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
116                 proc = (Form_pg_proc) GETSTRUCT(tup);
117                 finaltype = proc->prorettype;
118                 ReleaseSysCache(tup);
119         }
120         else
121         {
122                 /*
123                  * If no finalfn, aggregate result type is type of the state value
124                  */
125                 finaltype = aggTransType;
126         }
127         Assert(OidIsValid(finaltype));
128
129         /*
130          * Everything looks okay.  Try to create the pg_proc entry for the
131          * aggregate.  (This could fail if there's already a conflicting entry.)
132          */
133         MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
134         fnArgs[0] = aggBaseType;
135
136         procOid = ProcedureCreate(aggName,
137                                                           aggNamespace,
138                                                           false,                /* no replacement */
139                                                           false,                /* doesn't return a set */
140                                                           finaltype,    /* returnType */
141                                                           INTERNALlanguageId,   /* languageObjectId */
142                                                           0,
143                                                           "aggregate_dummy",    /* placeholder proc */
144                                                           "-",                  /* probin */
145                                                           true,                 /* isAgg */
146                                                           false,                /* security invoker (currently not definable for agg) */
147                                                           false,                /* isStrict (not needed for agg) */
148                                                           PROVOLATILE_IMMUTABLE,        /* volatility (not needed for agg) */
149                                                           1,                    /* parameterCount */
150                                                           fnArgs);              /* parameterTypes */
151
152         /*
153          * Okay to create the pg_aggregate entry.
154          */
155
156         /* initialize nulls and values */
157         for (i = 0; i < Natts_pg_aggregate; i++)
158         {
159                 nulls[i] = ' ';
160                 values[i] = (Datum) NULL;
161         }
162         values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
163         values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
164         values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
165         values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
166         if (agginitval)
167                 values[Anum_pg_aggregate_agginitval - 1] =
168                         DirectFunctionCall1(textin, CStringGetDatum(agginitval));
169         else
170                 nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
171
172         aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
173         tupDesc = aggdesc->rd_att;
174
175         tup = heap_formtuple(tupDesc, values, nulls);
176         simple_heap_insert(aggdesc, tup);
177
178         CatalogUpdateIndexes(aggdesc, tup);
179
180         heap_close(aggdesc, RowExclusiveLock);
181
182         /*
183          * Create dependencies for the aggregate (above and beyond those
184          * already made by ProcedureCreate).  Note: we don't need an explicit
185          * dependency on aggTransType since we depend on it indirectly through
186          * transfn.
187          */
188         myself.classId = RelOid_pg_proc;
189         myself.objectId = procOid;
190         myself.objectSubId = 0;
191
192         /* Depends on transition function */
193         referenced.classId = RelOid_pg_proc;
194         referenced.objectId = transfn;
195         referenced.objectSubId = 0;
196         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
197
198         /* Depends on final function, if any */
199         if (OidIsValid(finalfn))
200         {
201                 referenced.classId = RelOid_pg_proc;
202                 referenced.objectId = finalfn;
203                 referenced.objectSubId = 0;
204                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
205         }
206 }