]> granicus.if.org Git - postgresql/blob - src/backend/commands/aggregatecmds.c
Support renaming of tablespaces, and changing the owners of
[postgresql] / src / backend / commands / aggregatecmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * aggregatecmds.c
4  *
5  *        Routines for aggregate-manipulation commands
6  *
7  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.19 2004/06/25 21:55:53 tgl Exp $
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 "catalog/catname.h"
27 #include "catalog/dependency.h"
28 #include "catalog/indexing.h"
29 #include "catalog/namespace.h"
30 #include "catalog/pg_aggregate.h"
31 #include "catalog/pg_proc.h"
32 #include "catalog/pg_type.h"
33 #include "commands/defrem.h"
34 #include "miscadmin.h"
35 #include "parser/parse_func.h"
36 #include "parser/parse_type.h"
37 #include "utils/acl.h"
38 #include "utils/builtins.h"
39 #include "utils/lsyscache.h"
40 #include "utils/syscache.h"
41
42
43 /*
44  *      DefineAggregate
45  */
46 void
47 DefineAggregate(List *names, List *parameters)
48 {
49         char       *aggName;
50         Oid                     aggNamespace;
51         AclResult       aclresult;
52         List       *transfuncName = NIL;
53         List       *finalfuncName = NIL;
54         TypeName   *baseType = NULL;
55         TypeName   *transType = NULL;
56         char       *initval = NULL;
57         Oid                     baseTypeId;
58         Oid                     transTypeId;
59         ListCell   *pl;
60
61         /* Convert list of names to a name and namespace */
62         aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
63
64         /* Check we have creation rights in target namespace */
65         aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
66         if (aclresult != ACLCHECK_OK)
67                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
68                                            get_namespace_name(aggNamespace));
69
70         foreach(pl, parameters)
71         {
72                 DefElem    *defel = (DefElem *) lfirst(pl);
73
74                 /*
75                  * sfunc1, stype1, and initcond1 are accepted as obsolete
76                  * spellings for sfunc, stype, initcond.
77                  */
78                 if (pg_strcasecmp(defel->defname, "sfunc") == 0)
79                         transfuncName = defGetQualifiedName(defel);
80                 else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
81                         transfuncName = defGetQualifiedName(defel);
82                 else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
83                         finalfuncName = defGetQualifiedName(defel);
84                 else if (pg_strcasecmp(defel->defname, "basetype") == 0)
85                         baseType = defGetTypeName(defel);
86                 else if (pg_strcasecmp(defel->defname, "stype") == 0)
87                         transType = defGetTypeName(defel);
88                 else if (pg_strcasecmp(defel->defname, "stype1") == 0)
89                         transType = defGetTypeName(defel);
90                 else if (pg_strcasecmp(defel->defname, "initcond") == 0)
91                         initval = defGetString(defel);
92                 else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
93                         initval = defGetString(defel);
94                 else
95                         ereport(WARNING,
96                                         (errcode(ERRCODE_SYNTAX_ERROR),
97                                          errmsg("aggregate attribute \"%s\" not recognized",
98                                                         defel->defname)));
99         }
100
101         /*
102          * make sure we have our required definitions
103          */
104         if (baseType == NULL)
105                 ereport(ERROR,
106                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
107                                  errmsg("aggregate basetype must be specified")));
108         if (transType == NULL)
109                 ereport(ERROR,
110                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
111                                  errmsg("aggregate stype must be specified")));
112         if (transfuncName == NIL)
113                 ereport(ERROR,
114                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
115                                  errmsg("aggregate sfunc must be specified")));
116
117         /*
118          * look up the aggregate's base type (input datatype) and transtype.
119          *
120          * We have historically allowed the command to look like basetype = 'ANY'
121          * so we must do a case-insensitive comparison for the name ANY.  Ugh.
122          *
123          * basetype can be a pseudo-type, but transtype can't, since we need to
124          * be able to store values of the transtype.  However, we can allow
125          * polymorphic transtype in some cases (AggregateCreate will check).
126          */
127         if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
128                 baseTypeId = ANYOID;
129         else
130                 baseTypeId = typenameTypeId(baseType);
131
132         transTypeId = typenameTypeId(transType);
133         if (get_typtype(transTypeId) == 'p' &&
134                 transTypeId != ANYARRAYOID &&
135                 transTypeId != ANYELEMENTOID)
136                 ereport(ERROR,
137                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
138                                  errmsg("aggregate transition data type cannot be %s",
139                                                 format_type_be(transTypeId))));
140
141         /*
142          * Most of the argument-checking is done inside of AggregateCreate
143          */
144         AggregateCreate(aggName,        /* aggregate name */
145                                         aggNamespace,           /* namespace */
146                                         transfuncName,          /* step function name */
147                                         finalfuncName,          /* final function name */
148                                         baseTypeId, /* type of data being aggregated */
149                                         transTypeId,    /* transition data type */
150                                         initval);       /* initial condition */
151 }
152
153
154 /*
155  * RemoveAggregate
156  *              Deletes an aggregate.
157  */
158 void
159 RemoveAggregate(RemoveAggrStmt *stmt)
160 {
161         List       *aggName = stmt->aggname;
162         TypeName   *aggType = stmt->aggtype;
163         Oid                     basetypeID;
164         Oid                     procOid;
165         HeapTuple       tup;
166         ObjectAddress object;
167
168         /*
169          * if a basetype is passed in, then attempt to find an aggregate for
170          * that specific type.
171          *
172          * else attempt to find an aggregate with a basetype of ANYOID. This
173          * means that the aggregate is to apply to all basetypes (eg, COUNT).
174          */
175         if (aggType)
176                 basetypeID = typenameTypeId(aggType);
177         else
178                 basetypeID = ANYOID;
179
180         procOid = find_aggregate_func(aggName, basetypeID, false);
181
182         /*
183          * Find the function tuple, do permissions and validity checks
184          */
185         tup = SearchSysCache(PROCOID,
186                                                  ObjectIdGetDatum(procOid),
187                                                  0, 0, 0);
188         if (!HeapTupleIsValid(tup)) /* should not happen */
189                 elog(ERROR, "cache lookup failed for function %u", procOid);
190
191         /* Permission check: must own agg or its namespace */
192         if (!pg_proc_ownercheck(procOid, GetUserId()) &&
193                 !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
194                                                                  GetUserId()))
195                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
196                                            NameListToString(aggName));
197
198         /* find_aggregate_func already checked it is an aggregate */
199
200         ReleaseSysCache(tup);
201
202         /*
203          * Do the deletion
204          */
205         object.classId = RelOid_pg_proc;
206         object.objectId = procOid;
207         object.objectSubId = 0;
208
209         performDeletion(&object, stmt->behavior);
210 }
211
212
213 void
214 RenameAggregate(List *name, TypeName *basetype, const char *newname)
215 {
216         Oid                     basetypeOid;
217         Oid                     procOid;
218         Oid                     namespaceOid;
219         HeapTuple       tup;
220         Form_pg_proc procForm;
221         Relation        rel;
222         AclResult       aclresult;
223
224         /*
225          * if a basetype is passed in, then attempt to find an aggregate for
226          * that specific type; else attempt to find an aggregate with a basetype
227          * of ANYOID. This means that the aggregate applies to all basetypes
228          * (eg, COUNT).
229          */
230         if (basetype)
231                 basetypeOid = typenameTypeId(basetype);
232         else
233                 basetypeOid = ANYOID;
234
235         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
236
237         procOid = find_aggregate_func(name, basetypeOid, false);
238
239         tup = SearchSysCacheCopy(PROCOID,
240                                                          ObjectIdGetDatum(procOid),
241                                                          0, 0, 0);
242         if (!HeapTupleIsValid(tup)) /* should not happen */
243                 elog(ERROR, "cache lookup failed for function %u", procOid);
244         procForm = (Form_pg_proc) GETSTRUCT(tup);
245
246         namespaceOid = procForm->pronamespace;
247
248         /* make sure the new name doesn't exist */
249         if (SearchSysCacheExists(PROCNAMENSP,
250                                                          CStringGetDatum(newname),
251                                                          Int16GetDatum(procForm->pronargs),
252                                                          PointerGetDatum(procForm->proargtypes),
253                                                          ObjectIdGetDatum(namespaceOid)))
254         {
255                 if (basetypeOid == ANYOID)
256                         ereport(ERROR,
257                                         (errcode(ERRCODE_DUPLICATE_FUNCTION),
258                                  errmsg("function %s(*) already exists in schema \"%s\"",
259                                                 newname,
260                                                 get_namespace_name(namespaceOid))));
261                 else
262                         ereport(ERROR,
263                                         (errcode(ERRCODE_DUPLICATE_FUNCTION),
264                                          errmsg("function %s already exists in schema \"%s\"",
265                                                         funcname_signature_string(newname,
266                                                                                                           procForm->pronargs,
267                                                                                                   procForm->proargtypes),
268                                                         get_namespace_name(namespaceOid))));
269         }
270
271         /* must be owner */
272         if (!pg_proc_ownercheck(procOid, GetUserId()))
273                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
274                                            NameListToString(name));
275
276         /* must have CREATE privilege on namespace */
277         aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
278         if (aclresult != ACLCHECK_OK)
279                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
280                                            get_namespace_name(namespaceOid));
281
282         /* rename */
283         namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
284         simple_heap_update(rel, &tup->t_self, tup);
285         CatalogUpdateIndexes(rel, tup);
286
287         heap_close(rel, NoLock);
288         heap_freetuple(tup);
289 }
290
291 /*
292  * Change aggregate owner
293  */
294 void
295 AlterAggregateOwner(List *name, TypeName *basetype, AclId newOwnerSysId)
296 {
297         Oid                     basetypeOid;
298         Oid                     procOid;
299         HeapTuple       tup;
300         Form_pg_proc procForm;
301         Relation        rel;
302
303         /*
304          * if a basetype is passed in, then attempt to find an aggregate for
305          * that specific type; else attempt to find an aggregate with a basetype
306          * of ANYOID. This means that the aggregate applies to all basetypes
307          * (eg, COUNT).
308          */
309         if (basetype)
310                 basetypeOid = typenameTypeId(basetype);
311         else
312                 basetypeOid = ANYOID;
313
314         rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
315
316         procOid = find_aggregate_func(name, basetypeOid, false);
317
318         tup = SearchSysCacheCopy(PROCOID,
319                                                          ObjectIdGetDatum(procOid),
320                                                          0, 0, 0);
321         if (!HeapTupleIsValid(tup)) /* should not happen */
322                 elog(ERROR, "cache lookup failed for function %u", procOid);
323         procForm = (Form_pg_proc) GETSTRUCT(tup);
324
325         /* 
326          * If the new owner is the same as the existing owner, consider the
327          * command to have succeeded.  This is for dump restoration purposes.
328          */
329         if (procForm->proowner != newOwnerSysId)
330         {
331                 /* Otherwise, must be superuser to change object ownership */
332                 if (!superuser())
333                         ereport(ERROR,
334                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
335                                          errmsg("must be superuser to change owner")));
336
337                 /* Modify the owner --- okay to scribble on tup because it's a copy */
338                 procForm->proowner = newOwnerSysId;
339
340                 simple_heap_update(rel, &tup->t_self, tup);
341                 CatalogUpdateIndexes(rel, tup);
342         }
343
344         heap_close(rel, NoLock);
345         heap_freetuple(tup);
346 }