]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_aggregate.c
Extend pg_cast castimplicit column to a three-way value; this allows us
[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.56 2002/09/18 21:35:20 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), so
93          * 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 (!IsBinaryCoercible(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
132          * entry.)
133          */
134         MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
135         fnArgs[0] = aggBaseType;
136
137         procOid = ProcedureCreate(aggName,
138                                                           aggNamespace,
139                                                           false,        /* no replacement */
140                                                           false,        /* doesn't return a set */
141                                                           finaltype,            /* returnType */
142                                                           INTERNALlanguageId,           /* languageObjectId */
143                                                           0,
144                                                           "aggregate_dummy",            /* placeholder proc */
145                                                           "-",          /* probin */
146                                                           true,         /* isAgg */
147                                                           false,        /* security invoker (currently not
148                                                                                  * definable for agg) */
149                                                           false,        /* isStrict (not needed for agg) */
150                                                           PROVOLATILE_IMMUTABLE,        /* volatility (not
151                                                                                                                  * needed for agg) */
152                                                           1,    /* parameterCount */
153                                                           fnArgs);      /* parameterTypes */
154
155         /*
156          * Okay to create the pg_aggregate entry.
157          */
158
159         /* initialize nulls and values */
160         for (i = 0; i < Natts_pg_aggregate; i++)
161         {
162                 nulls[i] = ' ';
163                 values[i] = (Datum) NULL;
164         }
165         values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
166         values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
167         values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
168         values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
169         if (agginitval)
170                 values[Anum_pg_aggregate_agginitval - 1] =
171                         DirectFunctionCall1(textin, CStringGetDatum(agginitval));
172         else
173                 nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
174
175         aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
176         tupDesc = aggdesc->rd_att;
177
178         tup = heap_formtuple(tupDesc, values, nulls);
179         simple_heap_insert(aggdesc, tup);
180
181         CatalogUpdateIndexes(aggdesc, tup);
182
183         heap_close(aggdesc, RowExclusiveLock);
184
185         /*
186          * Create dependencies for the aggregate (above and beyond those
187          * already made by ProcedureCreate).  Note: we don't need an explicit
188          * dependency on aggTransType since we depend on it indirectly through
189          * transfn.
190          */
191         myself.classId = RelOid_pg_proc;
192         myself.objectId = procOid;
193         myself.objectSubId = 0;
194
195         /* Depends on transition function */
196         referenced.classId = RelOid_pg_proc;
197         referenced.objectId = transfn;
198         referenced.objectSubId = 0;
199         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
200
201         /* Depends on final function, if any */
202         if (OidIsValid(finalfn))
203         {
204                 referenced.classId = RelOid_pg_proc;
205                 referenced.objectId = finalfn;
206                 referenced.objectSubId = 0;
207                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
208         }
209 }