]> granicus.if.org Git - postgresql/blob - src/backend/commands/aggregatecmds.c
c99c07c454937e8a3e1605090ad4f393f1046c6f
[postgresql] / src / backend / commands / aggregatecmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * aggregatecmds.c
4  *
5  *        Routines for aggregate-manipulation commands
6  *
7  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        src/backend/commands/aggregatecmds.c
13  *
14  * DESCRIPTION
15  *        The "DefineFoo" routines take the parse tree and pick out the
16  *        appropriate arguments/flags, passing the results to the
17  *        corresponding "FooDefine" routines (in src/catalog) that do
18  *        the actual catalog-munging.  These routines also verify permission
19  *        of the user to execute the command.
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "access/htup_details.h"
27 #include "catalog/dependency.h"
28 #include "catalog/indexing.h"
29 #include "catalog/pg_aggregate.h"
30 #include "catalog/pg_proc.h"
31 #include "catalog/pg_type.h"
32 #include "commands/defrem.h"
33 #include "miscadmin.h"
34 #include "parser/parse_func.h"
35 #include "parser/parse_type.h"
36 #include "utils/acl.h"
37 #include "utils/builtins.h"
38 #include "utils/lsyscache.h"
39 #include "utils/syscache.h"
40
41
42 /*
43  *      DefineAggregate
44  *
45  * "oldstyle" signals the old (pre-8.2) style where the aggregate input type
46  * is specified by a BASETYPE element in the parameters.  Otherwise,
47  * "args" defines the input type(s).
48  */
49 void
50 DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
51 {
52         char       *aggName;
53         Oid                     aggNamespace;
54         AclResult       aclresult;
55         List       *transfuncName = NIL;
56         List       *finalfuncName = NIL;
57         List       *sortoperatorName = NIL;
58         TypeName   *baseType = NULL;
59         TypeName   *transType = NULL;
60         char       *initval = NULL;
61         Oid                *aggArgTypes;
62         int                     numArgs;
63         Oid                     transTypeId;
64         ListCell   *pl;
65
66         /* Convert list of names to a name and namespace */
67         aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
68
69         /* Check we have creation rights in target namespace */
70         aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
71         if (aclresult != ACLCHECK_OK)
72                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
73                                            get_namespace_name(aggNamespace));
74
75         foreach(pl, parameters)
76         {
77                 DefElem    *defel = (DefElem *) lfirst(pl);
78
79                 /*
80                  * sfunc1, stype1, and initcond1 are accepted as obsolete spellings
81                  * for sfunc, stype, initcond.
82                  */
83                 if (pg_strcasecmp(defel->defname, "sfunc") == 0)
84                         transfuncName = defGetQualifiedName(defel);
85                 else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
86                         transfuncName = defGetQualifiedName(defel);
87                 else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
88                         finalfuncName = defGetQualifiedName(defel);
89                 else if (pg_strcasecmp(defel->defname, "sortop") == 0)
90                         sortoperatorName = defGetQualifiedName(defel);
91                 else if (pg_strcasecmp(defel->defname, "basetype") == 0)
92                         baseType = defGetTypeName(defel);
93                 else if (pg_strcasecmp(defel->defname, "stype") == 0)
94                         transType = defGetTypeName(defel);
95                 else if (pg_strcasecmp(defel->defname, "stype1") == 0)
96                         transType = defGetTypeName(defel);
97                 else if (pg_strcasecmp(defel->defname, "initcond") == 0)
98                         initval = defGetString(defel);
99                 else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
100                         initval = defGetString(defel);
101                 else
102                         ereport(WARNING,
103                                         (errcode(ERRCODE_SYNTAX_ERROR),
104                                          errmsg("aggregate attribute \"%s\" not recognized",
105                                                         defel->defname)));
106         }
107
108         /*
109          * make sure we have our required definitions
110          */
111         if (transType == NULL)
112                 ereport(ERROR,
113                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
114                                  errmsg("aggregate stype must be specified")));
115         if (transfuncName == NIL)
116                 ereport(ERROR,
117                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
118                                  errmsg("aggregate sfunc must be specified")));
119
120         /*
121          * look up the aggregate's input datatype(s).
122          */
123         if (oldstyle)
124         {
125                 /*
126                  * Old style: use basetype parameter.  This supports aggregates of
127                  * zero or one input, with input type ANY meaning zero inputs.
128                  *
129                  * Historically we allowed the command to look like basetype = 'ANY'
130                  * so we must do a case-insensitive comparison for the name ANY. Ugh.
131                  */
132                 if (baseType == NULL)
133                         ereport(ERROR,
134                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
135                                          errmsg("aggregate input type must be specified")));
136
137                 if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
138                 {
139                         numArgs = 0;
140                         aggArgTypes = NULL;
141                 }
142                 else
143                 {
144                         numArgs = 1;
145                         aggArgTypes = (Oid *) palloc(sizeof(Oid));
146                         aggArgTypes[0] = typenameTypeId(NULL, baseType);
147                 }
148         }
149         else
150         {
151                 /*
152                  * New style: args is a list of TypeNames (possibly zero of 'em).
153                  */
154                 ListCell   *lc;
155                 int                     i = 0;
156
157                 if (baseType != NULL)
158                         ereport(ERROR,
159                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
160                                          errmsg("basetype is redundant with aggregate input type specification")));
161
162                 numArgs = list_length(args);
163                 aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
164                 foreach(lc, args)
165                 {
166                         TypeName   *curTypeName = (TypeName *) lfirst(lc);
167
168                         aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
169                 }
170         }
171
172         /*
173          * look up the aggregate's transtype.
174          *
175          * transtype can't be a pseudo-type, since we need to be able to store
176          * values of the transtype.  However, we can allow polymorphic transtype
177          * in some cases (AggregateCreate will check).  Also, we allow "internal"
178          * for functions that want to pass pointers to private data structures;
179          * but allow that only to superusers, since you could crash the system (or
180          * worse) by connecting up incompatible internal-using functions in an
181          * aggregate.
182          */
183         transTypeId = typenameTypeId(NULL, transType);
184         if (get_typtype(transTypeId) == TYPTYPE_PSEUDO &&
185                 !IsPolymorphicType(transTypeId))
186         {
187                 if (transTypeId == INTERNALOID && superuser())
188                          /* okay */ ;
189                 else
190                         ereport(ERROR,
191                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
192                                          errmsg("aggregate transition data type cannot be %s",
193                                                         format_type_be(transTypeId))));
194         }
195
196         /*
197          * Most of the argument-checking is done inside of AggregateCreate
198          */
199         AggregateCreate(aggName,        /* aggregate name */
200                                         aggNamespace,           /* namespace */
201                                         aggArgTypes,    /* input data type(s) */
202                                         numArgs,
203                                         transfuncName,          /* step function name */
204                                         finalfuncName,          /* final function name */
205                                         sortoperatorName,       /* sort operator name */
206                                         transTypeId,    /* transition data type */
207                                         initval);       /* initial condition */
208 }
209
210
211 /*
212  * RenameAggregate
213  *              Rename an aggregate.
214  */
215 void
216 RenameAggregate(List *name, List *args, const char *newname)
217 {
218         Oid                     procOid;
219         Oid                     namespaceOid;
220         HeapTuple       tup;
221         Form_pg_proc procForm;
222         Relation        rel;
223         AclResult       aclresult;
224
225         rel = heap_open(ProcedureRelationId, RowExclusiveLock);
226
227         /* Look up function and make sure it's an aggregate */
228         procOid = LookupAggNameTypeNames(name, args, false);
229
230         tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
231         if (!HeapTupleIsValid(tup)) /* should not happen */
232                 elog(ERROR, "cache lookup failed for function %u", procOid);
233         procForm = (Form_pg_proc) GETSTRUCT(tup);
234
235         namespaceOid = procForm->pronamespace;
236
237         /* make sure the new name doesn't exist */
238         if (SearchSysCacheExists3(PROCNAMEARGSNSP,
239                                                           CStringGetDatum(newname),
240                                                           PointerGetDatum(&procForm->proargtypes),
241                                                           ObjectIdGetDatum(namespaceOid)))
242                 ereport(ERROR,
243                                 (errcode(ERRCODE_DUPLICATE_FUNCTION),
244                                  errmsg("function %s already exists in schema \"%s\"",
245                                                 funcname_signature_string(newname,
246                                                                                                   procForm->pronargs,
247                                                                                                   NIL,
248                                                                                            procForm->proargtypes.values),
249                                                 get_namespace_name(namespaceOid))));
250
251         /* must be owner */
252         if (!pg_proc_ownercheck(procOid, GetUserId()))
253                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
254                                            NameListToString(name));
255
256         /* must have CREATE privilege on namespace */
257         aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
258         if (aclresult != ACLCHECK_OK)
259                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
260                                            get_namespace_name(namespaceOid));
261
262         /* rename */
263         namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
264         simple_heap_update(rel, &tup->t_self, tup);
265         CatalogUpdateIndexes(rel, tup);
266
267         heap_close(rel, NoLock);
268         heap_freetuple(tup);
269 }