OSDN Git Service

Update copyright for the year 2010.
[pg-rex/syncrep.git] / 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-2010, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.104 2010/01/02 16:57:36 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_aggregate.h"
21 #include "catalog/pg_language.h"
22 #include "catalog/pg_operator.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_proc_fn.h"
25 #include "catalog/pg_type.h"
26 #include "miscadmin.h"
27 #include "parser/parse_coerce.h"
28 #include "parser/parse_func.h"
29 #include "parser/parse_oper.h"
30 #include "utils/acl.h"
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
35
36
37 static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
38                                         Oid *rettype);
39
40
41 /*
42  * AggregateCreate
43  */
44 void
45 AggregateCreate(const char *aggName,
46                                 Oid aggNamespace,
47                                 Oid *aggArgTypes,
48                                 int numArgs,
49                                 List *aggtransfnName,
50                                 List *aggfinalfnName,
51                                 List *aggsortopName,
52                                 Oid aggTransType,
53                                 const char *agginitval)
54 {
55         Relation        aggdesc;
56         HeapTuple       tup;
57         bool            nulls[Natts_pg_aggregate];
58         Datum           values[Natts_pg_aggregate];
59         Form_pg_proc proc;
60         Oid                     transfn;
61         Oid                     finalfn = InvalidOid;   /* can be omitted */
62         Oid                     sortop = InvalidOid;    /* can be omitted */
63         bool            hasPolyArg;
64         bool            hasInternalArg;
65         Oid                     rettype;
66         Oid                     finaltype;
67         Oid                *fnArgs;
68         int                     nargs_transfn;
69         Oid                     procOid;
70         TupleDesc       tupDesc;
71         int                     i;
72         ObjectAddress myself,
73                                 referenced;
74
75         /* sanity checks (caller should have caught these) */
76         if (!aggName)
77                 elog(ERROR, "no aggregate name supplied");
78
79         if (!aggtransfnName)
80                 elog(ERROR, "aggregate must have a transition function");
81
82         /* check for polymorphic and INTERNAL arguments */
83         hasPolyArg = false;
84         hasInternalArg = false;
85         for (i = 0; i < numArgs; i++)
86         {
87                 if (IsPolymorphicType(aggArgTypes[i]))
88                         hasPolyArg = true;
89                 else if (aggArgTypes[i] == INTERNALOID)
90                         hasInternalArg = true;
91         }
92
93         /*
94          * If transtype is polymorphic, must have polymorphic argument also; else
95          * we will have no way to deduce the actual transtype.
96          */
97         if (IsPolymorphicType(aggTransType) && !hasPolyArg)
98                 ereport(ERROR,
99                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
100                                  errmsg("cannot determine transition data type"),
101                                  errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
102
103         /* find the transfn */
104         nargs_transfn = numArgs + 1;
105         fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
106         fnArgs[0] = aggTransType;
107         memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
108         transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
109                                                                   &rettype);
110
111         /*
112          * Return type of transfn (possibly after refinement by
113          * enforce_generic_type_consistency, if transtype isn't polymorphic) must
114          * exactly match declared transtype.
115          *
116          * In the non-polymorphic-transtype case, it might be okay to allow a
117          * rettype that's binary-coercible to transtype, but I'm not quite
118          * convinced that it's either safe or useful.  When transtype is
119          * polymorphic we *must* demand exact equality.
120          */
121         if (rettype != aggTransType)
122                 ereport(ERROR,
123                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
124                                  errmsg("return type of transition function %s is not %s",
125                                                 NameListToString(aggtransfnName),
126                                                 format_type_be(aggTransType))));
127
128         tup = SearchSysCache(PROCOID,
129                                                  ObjectIdGetDatum(transfn),
130                                                  0, 0, 0);
131         if (!HeapTupleIsValid(tup))
132                 elog(ERROR, "cache lookup failed for function %u", transfn);
133         proc = (Form_pg_proc) GETSTRUCT(tup);
134
135         /*
136          * If the transfn is strict and the initval is NULL, make sure first input
137          * type and transtype are the same (or at least binary-compatible), so
138          * that it's OK to use the first input value as the initial transValue.
139          */
140         if (proc->proisstrict && agginitval == NULL)
141         {
142                 if (numArgs < 1 ||
143                         !IsBinaryCoercible(aggArgTypes[0], aggTransType))
144                         ereport(ERROR,
145                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
146                                          errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
147         }
148         ReleaseSysCache(tup);
149
150         /* handle finalfn, if supplied */
151         if (aggfinalfnName)
152         {
153                 fnArgs[0] = aggTransType;
154                 finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
155                                                                           &finaltype);
156         }
157         else
158         {
159                 /*
160                  * If no finalfn, aggregate result type is type of the state value
161                  */
162                 finaltype = aggTransType;
163         }
164         Assert(OidIsValid(finaltype));
165
166         /*
167          * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
168          * be polymorphic also, else parser will fail to deduce result type.
169          * (Note: given the previous test on transtype and inputs, this cannot
170          * happen, unless someone has snuck a finalfn definition into the catalogs
171          * that itself violates the rule against polymorphic result with no
172          * polymorphic input.)
173          */
174         if (IsPolymorphicType(finaltype) && !hasPolyArg)
175                 ereport(ERROR,
176                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
177                                  errmsg("cannot determine result data type"),
178                                  errdetail("An aggregate returning a polymorphic type "
179                                                    "must have at least one polymorphic argument.")));
180
181         /*
182          * Also, the return type can't be INTERNAL unless there's at least one
183          * INTERNAL argument.  This is the same type-safety restriction we enforce
184          * for regular functions, but at the level of aggregates.  We must test
185          * this explicitly because we allow INTERNAL as the transtype.
186          */
187         if (finaltype == INTERNALOID && !hasInternalArg)
188                 ereport(ERROR,
189                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
190                                  errmsg("unsafe use of pseudo-type \"internal\""),
191                                  errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
192
193         /* handle sortop, if supplied */
194         if (aggsortopName)
195         {
196                 if (numArgs != 1)
197                         ereport(ERROR,
198                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
199                                          errmsg("sort operator can only be specified for single-argument aggregates")));
200                 sortop = LookupOperName(NULL, aggsortopName,
201                                                                 aggArgTypes[0], aggArgTypes[0],
202                                                                 false, -1);
203         }
204
205         /*
206          * Everything looks okay.  Try to create the pg_proc entry for the
207          * aggregate.  (This could fail if there's already a conflicting entry.)
208          */
209
210         procOid = ProcedureCreate(aggName,
211                                                           aggNamespace,
212                                                           false,        /* no replacement */
213                                                           false,        /* doesn't return a set */
214                                                           finaltype,            /* returnType */
215                                                           INTERNALlanguageId,           /* languageObjectId */
216                                                           InvalidOid,           /* no validator */
217                                                           "aggregate_dummy",            /* placeholder proc */
218                                                           NULL,         /* probin */
219                                                           true,         /* isAgg */
220                                                           false,        /* isWindowFunc */
221                                                           false,        /* security invoker (currently not
222                                                                                  * definable for agg) */
223                                                           false,        /* isStrict (not needed for agg) */
224                                                           PROVOLATILE_IMMUTABLE,        /* volatility (not
225                                                                                                                  * needed for agg) */
226                                                           buildoidvector(aggArgTypes,
227                                                                                          numArgs),      /* paramTypes */
228                                                           PointerGetDatum(NULL),        /* allParamTypes */
229                                                           PointerGetDatum(NULL),        /* parameterModes */
230                                                           PointerGetDatum(NULL),        /* parameterNames */
231                                                           NIL,          /* parameterDefaults */
232                                                           PointerGetDatum(NULL),        /* proconfig */
233                                                           1,    /* procost */
234                                                           0);           /* prorows */
235
236         /*
237          * Okay to create the pg_aggregate entry.
238          */
239
240         /* initialize nulls and values */
241         for (i = 0; i < Natts_pg_aggregate; i++)
242         {
243                 nulls[i] = false;
244                 values[i] = (Datum) NULL;
245         }
246         values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
247         values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
248         values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
249         values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
250         values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
251         if (agginitval)
252                 values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
253         else
254                 nulls[Anum_pg_aggregate_agginitval - 1] = true;
255
256         aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
257         tupDesc = aggdesc->rd_att;
258
259         tup = heap_form_tuple(tupDesc, values, nulls);
260         simple_heap_insert(aggdesc, tup);
261
262         CatalogUpdateIndexes(aggdesc, tup);
263
264         heap_close(aggdesc, RowExclusiveLock);
265
266         /*
267          * Create dependencies for the aggregate (above and beyond those already
268          * made by ProcedureCreate).  Note: we don't need an explicit dependency
269          * on aggTransType since we depend on it indirectly through transfn.
270          */
271         myself.classId = ProcedureRelationId;
272         myself.objectId = procOid;
273         myself.objectSubId = 0;
274
275         /* Depends on transition function */
276         referenced.classId = ProcedureRelationId;
277         referenced.objectId = transfn;
278         referenced.objectSubId = 0;
279         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
280
281         /* Depends on final function, if any */
282         if (OidIsValid(finalfn))
283         {
284                 referenced.classId = ProcedureRelationId;
285                 referenced.objectId = finalfn;
286                 referenced.objectSubId = 0;
287                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
288         }
289
290         /* Depends on sort operator, if any */
291         if (OidIsValid(sortop))
292         {
293                 referenced.classId = OperatorRelationId;
294                 referenced.objectId = sortop;
295                 referenced.objectSubId = 0;
296                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
297         }
298 }
299
300 /*
301  * lookup_agg_function -- common code for finding both transfn and finalfn
302  */
303 static Oid
304 lookup_agg_function(List *fnName,
305                                         int nargs,
306                                         Oid *input_types,
307                                         Oid *rettype)
308 {
309         Oid                     fnOid;
310         bool            retset;
311         int                     nvargs;
312         Oid                *true_oid_array;
313         FuncDetailCode fdresult;
314         AclResult       aclresult;
315         int                     i;
316
317         /*
318          * func_get_detail looks up the function in the catalogs, does
319          * disambiguation for polymorphic functions, handles inheritance, and
320          * returns the funcid and type and set or singleton status of the
321          * function's return value.  it also returns the true argument types to
322          * the function.
323          */
324         fdresult = func_get_detail(fnName, NIL, NIL,
325                                                            nargs, input_types, false, false,
326                                                            &fnOid, rettype, &retset, &nvargs,
327                                                            &true_oid_array, NULL);
328
329         /* only valid case is a normal function not returning a set */
330         if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
331                 ereport(ERROR,
332                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
333                                  errmsg("function %s does not exist",
334                                                 func_signature_string(fnName, nargs,
335                                                                                           NIL, input_types))));
336         if (retset)
337                 ereport(ERROR,
338                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
339                                  errmsg("function %s returns a set",
340                                                 func_signature_string(fnName, nargs,
341                                                                                           NIL, input_types))));
342
343         /*
344          * If there are any polymorphic types involved, enforce consistency, and
345          * possibly refine the result type.  It's OK if the result is still
346          * polymorphic at this point, though.
347          */
348         *rettype = enforce_generic_type_consistency(input_types,
349                                                                                                 true_oid_array,
350                                                                                                 nargs,
351                                                                                                 *rettype,
352                                                                                                 true);
353
354         /*
355          * func_get_detail will find functions requiring run-time argument type
356          * coercion, but nodeAgg.c isn't prepared to deal with that
357          */
358         for (i = 0; i < nargs; i++)
359         {
360                 if (!IsPolymorphicType(true_oid_array[i]) &&
361                         !IsBinaryCoercible(input_types[i], true_oid_array[i]))
362                         ereport(ERROR,
363                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
364                                          errmsg("function %s requires run-time type coercion",
365                                          func_signature_string(fnName, nargs,
366                                                                                    NIL, true_oid_array))));
367         }
368
369         /* Check aggregate creator has permission to call the function */
370         aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
371         if (aclresult != ACLCHECK_OK)
372                 aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));
373
374         return fnOid;
375 }