]> granicus.if.org Git - postgresql/blob - src/backend/commands/operatorcmds.c
Improve reporting of permission errors for array types
[postgresql] / src / backend / commands / operatorcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * operatorcmds.c
4  *
5  *        Routines for operator 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/operatorcmds.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  * NOTES
22  *        These things must be defined and committed in the following order:
23  *              "create function":
24  *                              input/output, recv/send procedures
25  *              "create type":
26  *                              type
27  *              "create operator":
28  *                              operators
29  *
30  *              Most of the parse-tree manipulation routines are defined in
31  *              commands/manip.c.
32  *
33  *-------------------------------------------------------------------------
34  */
35 #include "postgres.h"
36
37 #include "access/heapam.h"
38 #include "catalog/dependency.h"
39 #include "catalog/indexing.h"
40 #include "catalog/pg_operator.h"
41 #include "catalog/pg_type.h"
42 #include "commands/alter.h"
43 #include "commands/defrem.h"
44 #include "miscadmin.h"
45 #include "parser/parse_func.h"
46 #include "parser/parse_oper.h"
47 #include "parser/parse_type.h"
48 #include "utils/builtins.h"
49 #include "utils/lsyscache.h"
50 #include "utils/rel.h"
51 #include "utils/syscache.h"
52
53
54 static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
55
56 /*
57  * DefineOperator
58  *              this function extracts all the information from the
59  *              parameter list generated by the parser and then has
60  *              OperatorCreate() do all the actual work.
61  *
62  * 'parameters' is a list of DefElem
63  */
64 void
65 DefineOperator(List *names, List *parameters)
66 {
67         char       *oprName;
68         Oid                     oprNamespace;
69         AclResult       aclresult;
70         bool            canMerge = false;               /* operator merges */
71         bool            canHash = false;        /* operator hashes */
72         List       *functionName = NIL;         /* function for operator */
73         TypeName   *typeName1 = NULL;           /* first type name */
74         TypeName   *typeName2 = NULL;           /* second type name */
75         Oid                     typeId1 = InvalidOid;   /* types converted to OID */
76         Oid                     typeId2 = InvalidOid;
77         Oid                     rettype;
78         List       *commutatorName = NIL;       /* optional commutator operator name */
79         List       *negatorName = NIL;          /* optional negator operator name */
80         List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
81         List       *joinName = NIL; /* optional join sel. procedure */
82         Oid                     functionOid;    /* functions converted to OID */
83         Oid                     restrictionOid;
84         Oid                     joinOid;
85         Oid                     typeId[5];              /* only need up to 5 args here */
86         int                     nargs;
87         ListCell   *pl;
88
89         /* Convert list of names to a name and namespace */
90         oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
91
92         /*
93          * The SQL standard committee has decided that => should be used for named
94          * parameters; therefore, a future release of PostgreSQL may disallow it
95          * as the name of a user-defined operator.
96          */
97         if (strcmp(oprName, "=>") == 0)
98                 ereport(WARNING,
99                                 (errmsg("=> is deprecated as an operator name"),
100                                  errdetail("This name may be disallowed altogether in future versions of PostgreSQL.")));
101
102         /* Check we have creation rights in target namespace */
103         aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
104         if (aclresult != ACLCHECK_OK)
105                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
106                                            get_namespace_name(oprNamespace));
107
108         /*
109          * loop over the definition list and extract the information we need.
110          */
111         foreach(pl, parameters)
112         {
113                 DefElem    *defel = (DefElem *) lfirst(pl);
114
115                 if (pg_strcasecmp(defel->defname, "leftarg") == 0)
116                 {
117                         typeName1 = defGetTypeName(defel);
118                         if (typeName1->setof)
119                                 ereport(ERROR,
120                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
121                                         errmsg("SETOF type not allowed for operator argument")));
122                 }
123                 else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
124                 {
125                         typeName2 = defGetTypeName(defel);
126                         if (typeName2->setof)
127                                 ereport(ERROR,
128                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
129                                         errmsg("SETOF type not allowed for operator argument")));
130                 }
131                 else if (pg_strcasecmp(defel->defname, "procedure") == 0)
132                         functionName = defGetQualifiedName(defel);
133                 else if (pg_strcasecmp(defel->defname, "commutator") == 0)
134                         commutatorName = defGetQualifiedName(defel);
135                 else if (pg_strcasecmp(defel->defname, "negator") == 0)
136                         negatorName = defGetQualifiedName(defel);
137                 else if (pg_strcasecmp(defel->defname, "restrict") == 0)
138                         restrictionName = defGetQualifiedName(defel);
139                 else if (pg_strcasecmp(defel->defname, "join") == 0)
140                         joinName = defGetQualifiedName(defel);
141                 else if (pg_strcasecmp(defel->defname, "hashes") == 0)
142                         canHash = defGetBoolean(defel);
143                 else if (pg_strcasecmp(defel->defname, "merges") == 0)
144                         canMerge = defGetBoolean(defel);
145                 /* These obsolete options are taken as meaning canMerge */
146                 else if (pg_strcasecmp(defel->defname, "sort1") == 0)
147                         canMerge = true;
148                 else if (pg_strcasecmp(defel->defname, "sort2") == 0)
149                         canMerge = true;
150                 else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
151                         canMerge = true;
152                 else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
153                         canMerge = true;
154                 else
155                         ereport(WARNING,
156                                         (errcode(ERRCODE_SYNTAX_ERROR),
157                                          errmsg("operator attribute \"%s\" not recognized",
158                                                         defel->defname)));
159         }
160
161         /*
162          * make sure we have our required definitions
163          */
164         if (functionName == NIL)
165                 ereport(ERROR,
166                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
167                                  errmsg("operator procedure must be specified")));
168
169         /* Transform type names to type OIDs */
170         if (typeName1)
171                 typeId1 = typenameTypeId(NULL, typeName1);
172         if (typeName2)
173                 typeId2 = typenameTypeId(NULL, typeName2);
174
175         if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
176                 ereport(ERROR,
177                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
178                    errmsg("at least one of leftarg or rightarg must be specified")));
179
180         if (typeName1)
181         {
182                 aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
183                 if (aclresult != ACLCHECK_OK)
184                         aclcheck_error_type(aclresult, typeId1);
185         }
186
187         if (typeName2)
188         {
189                 aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
190                 if (aclresult != ACLCHECK_OK)
191                         aclcheck_error_type(aclresult, typeId2);
192         }
193
194         /*
195          * Look up the operator's underlying function.
196          */
197         if (!OidIsValid(typeId1))
198         {
199                 typeId[0] = typeId2;
200                 nargs = 1;
201         }
202         else if (!OidIsValid(typeId2))
203         {
204                 typeId[0] = typeId1;
205                 nargs = 1;
206         }
207         else
208         {
209                 typeId[0] = typeId1;
210                 typeId[1] = typeId2;
211                 nargs = 2;
212         }
213         functionOid = LookupFuncName(functionName, nargs, typeId, false);
214
215         /*
216          * We require EXECUTE rights for the function.  This isn't strictly
217          * necessary, since EXECUTE will be checked at any attempted use of the
218          * operator, but it seems like a good idea anyway.
219          */
220         aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
221         if (aclresult != ACLCHECK_OK)
222                 aclcheck_error(aclresult, ACL_KIND_PROC,
223                                            NameListToString(functionName));
224
225         rettype = get_func_rettype(functionOid);
226         aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
227         if (aclresult != ACLCHECK_OK)
228                 aclcheck_error_type(aclresult, rettype);
229
230         /*
231          * Look up restriction estimator if specified
232          */
233         if (restrictionName)
234         {
235                 typeId[0] = INTERNALOID;        /* PlannerInfo */
236                 typeId[1] = OIDOID;             /* operator OID */
237                 typeId[2] = INTERNALOID;        /* args list */
238                 typeId[3] = INT4OID;    /* varRelid */
239
240                 restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
241
242                 /* estimators must return float8 */
243                 if (get_func_rettype(restrictionOid) != FLOAT8OID)
244                         ereport(ERROR,
245                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246                                          errmsg("restriction estimator function %s must return type \"float8\"",
247                                                         NameListToString(restrictionName))));
248
249                 /* Require EXECUTE rights for the estimator */
250                 aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
251                 if (aclresult != ACLCHECK_OK)
252                         aclcheck_error(aclresult, ACL_KIND_PROC,
253                                                    NameListToString(restrictionName));
254         }
255         else
256                 restrictionOid = InvalidOid;
257
258         /*
259          * Look up join estimator if specified
260          */
261         if (joinName)
262         {
263                 typeId[0] = INTERNALOID;        /* PlannerInfo */
264                 typeId[1] = OIDOID;             /* operator OID */
265                 typeId[2] = INTERNALOID;        /* args list */
266                 typeId[3] = INT2OID;    /* jointype */
267                 typeId[4] = INTERNALOID;        /* SpecialJoinInfo */
268
269                 /*
270                  * As of Postgres 8.4, the preferred signature for join estimators has
271                  * 5 arguments, but we still allow the old 4-argument form. Try the
272                  * preferred form first.
273                  */
274                 joinOid = LookupFuncName(joinName, 5, typeId, true);
275                 if (!OidIsValid(joinOid))
276                         joinOid = LookupFuncName(joinName, 4, typeId, true);
277                 /* If not found, reference the 5-argument signature in error msg */
278                 if (!OidIsValid(joinOid))
279                         joinOid = LookupFuncName(joinName, 5, typeId, false);
280
281                 /* estimators must return float8 */
282                 if (get_func_rettype(joinOid) != FLOAT8OID)
283                         ereport(ERROR,
284                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
285                          errmsg("join estimator function %s must return type \"float8\"",
286                                         NameListToString(joinName))));
287
288                 /* Require EXECUTE rights for the estimator */
289                 aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
290                 if (aclresult != ACLCHECK_OK)
291                         aclcheck_error(aclresult, ACL_KIND_PROC,
292                                                    NameListToString(joinName));
293         }
294         else
295                 joinOid = InvalidOid;
296
297         /*
298          * now have OperatorCreate do all the work..
299          */
300         OperatorCreate(oprName,         /* operator name */
301                                    oprNamespace,        /* namespace */
302                                    typeId1,             /* left type id */
303                                    typeId2,             /* right type id */
304                                    functionOid, /* function for operator */
305                                    commutatorName,              /* optional commutator operator name */
306                                    negatorName, /* optional negator operator name */
307                                    restrictionOid,              /* optional restrict. sel. procedure */
308                                    joinOid,             /* optional join sel. procedure name */
309                                    canMerge,    /* operator merges */
310                                    canHash);    /* operator hashes */
311 }
312
313
314 /*
315  * Guts of operator deletion.
316  */
317 void
318 RemoveOperatorById(Oid operOid)
319 {
320         Relation        relation;
321         HeapTuple       tup;
322
323         relation = heap_open(OperatorRelationId, RowExclusiveLock);
324
325         tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
326         if (!HeapTupleIsValid(tup)) /* should not happen */
327                 elog(ERROR, "cache lookup failed for operator %u", operOid);
328
329         simple_heap_delete(relation, &tup->t_self);
330
331         ReleaseSysCache(tup);
332
333         heap_close(relation, RowExclusiveLock);
334 }
335
336 void
337 AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
338 {
339         Relation        rel;
340
341         rel = heap_open(OperatorRelationId, RowExclusiveLock);
342
343         AlterOperatorOwner_internal(rel, operOid, newOwnerId);
344
345         heap_close(rel, NoLock);
346 }
347
348 /*
349  * change operator owner
350  */
351 void
352 AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
353                                    Oid newOwnerId)
354 {
355         Oid                     operOid;
356         Relation        rel;
357
358         rel = heap_open(OperatorRelationId, RowExclusiveLock);
359
360         operOid = LookupOperNameTypeNames(NULL, name,
361                                                                           typeName1, typeName2,
362                                                                           false, -1);
363
364         AlterOperatorOwner_internal(rel, operOid, newOwnerId);
365
366         heap_close(rel, NoLock);
367 }
368
369 static void
370 AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
371 {
372         HeapTuple       tup;
373         AclResult       aclresult;
374         Form_pg_operator oprForm;
375
376         Assert(RelationGetRelid(rel) == OperatorRelationId);
377
378         tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operOid));
379         if (!HeapTupleIsValid(tup)) /* should not happen */
380                 elog(ERROR, "cache lookup failed for operator %u", operOid);
381
382         oprForm = (Form_pg_operator) GETSTRUCT(tup);
383
384         /*
385          * If the new owner is the same as the existing owner, consider the
386          * command to have succeeded.  This is for dump restoration purposes.
387          */
388         if (oprForm->oprowner != newOwnerId)
389         {
390                 /* Superusers can always do it */
391                 if (!superuser())
392                 {
393                         /* Otherwise, must be owner of the existing object */
394                         if (!pg_oper_ownercheck(operOid, GetUserId()))
395                                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
396                                                            NameStr(oprForm->oprname));
397
398                         /* Must be able to become new owner */
399                         check_is_member_of_role(GetUserId(), newOwnerId);
400
401                         /* New owner must have CREATE privilege on namespace */
402                         aclresult = pg_namespace_aclcheck(oprForm->oprnamespace,
403                                                                                           newOwnerId,
404                                                                                           ACL_CREATE);
405                         if (aclresult != ACLCHECK_OK)
406                                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
407                                                            get_namespace_name(oprForm->oprnamespace));
408                 }
409
410                 /*
411                  * Modify the owner --- okay to scribble on tup because it's a copy
412                  */
413                 oprForm->oprowner = newOwnerId;
414
415                 simple_heap_update(rel, &tup->t_self, tup);
416
417                 CatalogUpdateIndexes(rel, tup);
418
419                 /* Update owner dependency reference */
420                 changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
421         }
422
423         heap_freetuple(tup);
424 }
425
426 /*
427  * Execute ALTER OPERATOR SET SCHEMA
428  */
429 void
430 AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
431 {
432         List       *operatorName = names;
433         TypeName   *typeName1 = (TypeName *) linitial(argtypes);
434         TypeName   *typeName2 = (TypeName *) lsecond(argtypes);
435         Oid                     operOid,
436                                 nspOid;
437         Relation        rel;
438
439         rel = heap_open(OperatorRelationId, RowExclusiveLock);
440
441         Assert(list_length(argtypes) == 2);
442         operOid = LookupOperNameTypeNames(NULL, operatorName,
443                                                                           typeName1, typeName2,
444                                                                           false, -1);
445
446         /* get schema OID */
447         nspOid = LookupCreationNamespace(newschema);
448
449         AlterObjectNamespace(rel, OPEROID, -1,
450                                                  operOid, nspOid,
451                                                  Anum_pg_operator_oprname,
452                                                  Anum_pg_operator_oprnamespace,
453                                                  Anum_pg_operator_oprowner,
454                                                  ACL_KIND_OPER);
455
456         heap_close(rel, RowExclusiveLock);
457 }
458
459 Oid
460 AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
461 {
462         Oid                     oldNspOid;
463         Relation        rel;
464
465         rel = heap_open(OperatorRelationId, RowExclusiveLock);
466
467         oldNspOid = AlterObjectNamespace(rel, OPEROID, -1,
468                                                                          operOid, newNspOid,
469                                                                          Anum_pg_operator_oprname,
470                                                                          Anum_pg_operator_oprnamespace,
471                                                                          Anum_pg_operator_oprowner,
472                                                                          ACL_KIND_OPER);
473
474         heap_close(rel, RowExclusiveLock);
475
476         return oldNspOid;
477 }