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