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