]> granicus.if.org Git - postgresql/blob - src/backend/commands/operatorcmds.c
Fix ALTER OPERATOR to update dependencies properly.
[postgresql] / src / backend / commands / operatorcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * operatorcmds.c
4  *
5  *        Routines for operator manipulation commands
6  *
7  * Portions Copyright (c) 1996-2015, 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 "access/htup_details.h"
39 #include "catalog/dependency.h"
40 #include "catalog/indexing.h"
41 #include "catalog/objectaccess.h"
42 #include "catalog/pg_operator.h"
43 #include "catalog/pg_operator_fn.h"
44 #include "catalog/pg_type.h"
45 #include "commands/alter.h"
46 #include "commands/defrem.h"
47 #include "miscadmin.h"
48 #include "parser/parse_func.h"
49 #include "parser/parse_oper.h"
50 #include "parser/parse_type.h"
51 #include "utils/builtins.h"
52 #include "utils/lsyscache.h"
53 #include "utils/rel.h"
54 #include "utils/syscache.h"
55
56 static Oid      ValidateRestrictionEstimator(List *restrictionName);
57 static Oid      ValidateJoinEstimator(List *joinName);
58
59 /*
60  * DefineOperator
61  *              this function extracts all the information from the
62  *              parameter list generated by the parser and then has
63  *              OperatorCreate() do all the actual work.
64  *
65  * 'parameters' is a list of DefElem
66  */
67 ObjectAddress
68 DefineOperator(List *names, List *parameters)
69 {
70         char       *oprName;
71         Oid                     oprNamespace;
72         AclResult       aclresult;
73         bool            canMerge = false;               /* operator merges */
74         bool            canHash = false;        /* operator hashes */
75         List       *functionName = NIL;         /* function for operator */
76         TypeName   *typeName1 = NULL;           /* first type name */
77         TypeName   *typeName2 = NULL;           /* second type name */
78         Oid                     typeId1 = InvalidOid;   /* types converted to OID */
79         Oid                     typeId2 = InvalidOid;
80         Oid                     rettype;
81         List       *commutatorName = NIL;       /* optional commutator operator name */
82         List       *negatorName = NIL;          /* optional negator operator name */
83         List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
84         List       *joinName = NIL; /* optional join sel. procedure */
85         Oid                     functionOid;    /* functions converted to OID */
86         Oid                     restrictionOid;
87         Oid                     joinOid;
88         Oid                     typeId[2];              /* to hold left and right arg */
89         int                     nargs;
90         ListCell   *pl;
91
92         /* Convert list of names to a name and namespace */
93         oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
94
95         /* Check we have creation rights in target namespace */
96         aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
97         if (aclresult != ACLCHECK_OK)
98                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
99                                            get_namespace_name(oprNamespace));
100
101         /*
102          * loop over the definition list and extract the information we need.
103          */
104         foreach(pl, parameters)
105         {
106                 DefElem    *defel = (DefElem *) lfirst(pl);
107
108                 if (pg_strcasecmp(defel->defname, "leftarg") == 0)
109                 {
110                         typeName1 = defGetTypeName(defel);
111                         if (typeName1->setof)
112                                 ereport(ERROR,
113                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
114                                         errmsg("SETOF type not allowed for operator argument")));
115                 }
116                 else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
117                 {
118                         typeName2 = defGetTypeName(defel);
119                         if (typeName2->setof)
120                                 ereport(ERROR,
121                                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
122                                         errmsg("SETOF type not allowed for operator argument")));
123                 }
124                 else if (pg_strcasecmp(defel->defname, "procedure") == 0)
125                         functionName = defGetQualifiedName(defel);
126                 else if (pg_strcasecmp(defel->defname, "commutator") == 0)
127                         commutatorName = defGetQualifiedName(defel);
128                 else if (pg_strcasecmp(defel->defname, "negator") == 0)
129                         negatorName = defGetQualifiedName(defel);
130                 else if (pg_strcasecmp(defel->defname, "restrict") == 0)
131                         restrictionName = defGetQualifiedName(defel);
132                 else if (pg_strcasecmp(defel->defname, "join") == 0)
133                         joinName = defGetQualifiedName(defel);
134                 else if (pg_strcasecmp(defel->defname, "hashes") == 0)
135                         canHash = defGetBoolean(defel);
136                 else if (pg_strcasecmp(defel->defname, "merges") == 0)
137                         canMerge = defGetBoolean(defel);
138                 /* These obsolete options are taken as meaning canMerge */
139                 else if (pg_strcasecmp(defel->defname, "sort1") == 0)
140                         canMerge = true;
141                 else if (pg_strcasecmp(defel->defname, "sort2") == 0)
142                         canMerge = true;
143                 else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
144                         canMerge = true;
145                 else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
146                         canMerge = true;
147                 else
148                 {
149                         /* WARNING, not ERROR, for historical backwards-compatibility */
150                         ereport(WARNING,
151                                         (errcode(ERRCODE_SYNTAX_ERROR),
152                                          errmsg("operator attribute \"%s\" not recognized",
153                                                         defel->defname)));
154                 }
155         }
156
157         /*
158          * make sure we have our required definitions
159          */
160         if (functionName == NIL)
161                 ereport(ERROR,
162                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
163                                  errmsg("operator procedure must be specified")));
164
165         /* Transform type names to type OIDs */
166         if (typeName1)
167                 typeId1 = typenameTypeId(NULL, typeName1);
168         if (typeName2)
169                 typeId2 = typenameTypeId(NULL, typeName2);
170
171         if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
172                 ereport(ERROR,
173                                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
174                    errmsg("at least one of leftarg or rightarg must be specified")));
175
176         if (typeName1)
177         {
178                 aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
179                 if (aclresult != ACLCHECK_OK)
180                         aclcheck_error_type(aclresult, typeId1);
181         }
182
183         if (typeName2)
184         {
185                 aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
186                 if (aclresult != ACLCHECK_OK)
187                         aclcheck_error_type(aclresult, typeId2);
188         }
189
190         /*
191          * Look up the operator's underlying function.
192          */
193         if (!OidIsValid(typeId1))
194         {
195                 typeId[0] = typeId2;
196                 nargs = 1;
197         }
198         else if (!OidIsValid(typeId2))
199         {
200                 typeId[0] = typeId1;
201                 nargs = 1;
202         }
203         else
204         {
205                 typeId[0] = typeId1;
206                 typeId[1] = typeId2;
207                 nargs = 2;
208         }
209         functionOid = LookupFuncName(functionName, nargs, typeId, false);
210
211         /*
212          * We require EXECUTE rights for the function.  This isn't strictly
213          * necessary, since EXECUTE will be checked at any attempted use of the
214          * operator, but it seems like a good idea anyway.
215          */
216         aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
217         if (aclresult != ACLCHECK_OK)
218                 aclcheck_error(aclresult, ACL_KIND_PROC,
219                                            NameListToString(functionName));
220
221         rettype = get_func_rettype(functionOid);
222         aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
223         if (aclresult != ACLCHECK_OK)
224                 aclcheck_error_type(aclresult, rettype);
225
226         /*
227          * Look up restriction and join estimators if specified
228          */
229         if (restrictionName)
230                 restrictionOid = ValidateRestrictionEstimator(restrictionName);
231         else
232                 restrictionOid = InvalidOid;
233         if (joinName)
234                 joinOid = ValidateJoinEstimator(joinName);
235         else
236                 joinOid = InvalidOid;
237
238         /*
239          * now have OperatorCreate do all the work..
240          */
241         return
242                 OperatorCreate(oprName, /* operator name */
243                                            oprNamespace,        /* namespace */
244                                            typeId1, /* left type id */
245                                            typeId2, /* right type id */
246                                            functionOid,         /* function for operator */
247                                            commutatorName,      /* optional commutator operator name */
248                                            negatorName,         /* optional negator operator name */
249                                            restrictionOid,      /* optional restrict. sel. procedure */
250                                            joinOid, /* optional join sel. procedure name */
251                                            canMerge,    /* operator merges */
252                                            canHash);    /* operator hashes */
253 }
254
255 /*
256  * Look up a restriction estimator function ny name, and verify that it has
257  * the correct signature and we have the permissions to attach it to an
258  * operator.
259  */
260 static Oid
261 ValidateRestrictionEstimator(List *restrictionName)
262 {
263         Oid                     typeId[4];
264         Oid                     restrictionOid;
265         AclResult       aclresult;
266
267         typeId[0] = INTERNALOID;        /* PlannerInfo */
268         typeId[1] = OIDOID;                     /* operator OID */
269         typeId[2] = INTERNALOID;        /* args list */
270         typeId[3] = INT4OID;            /* varRelid */
271
272         restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
273
274         /* estimators must return float8 */
275         if (get_func_rettype(restrictionOid) != FLOAT8OID)
276                 ereport(ERROR,
277                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
278                                  errmsg("restriction estimator function %s must return type \"float8\"",
279                                                 NameListToString(restrictionName))));
280
281         /* Require EXECUTE rights for the estimator */
282         aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
283         if (aclresult != ACLCHECK_OK)
284                 aclcheck_error(aclresult, ACL_KIND_PROC,
285                                            NameListToString(restrictionName));
286
287         return restrictionOid;
288 }
289
290 /*
291  * Look up a join estimator function ny name, and verify that it has the
292  * correct signature and we have the permissions to attach it to an
293  * operator.
294  */
295 static Oid
296 ValidateJoinEstimator(List *joinName)
297 {
298         Oid                     typeId[5];
299         Oid                     joinOid;
300         AclResult       aclresult;
301
302         typeId[0] = INTERNALOID;        /* PlannerInfo */
303         typeId[1] = OIDOID;                     /* operator OID */
304         typeId[2] = INTERNALOID;        /* args list */
305         typeId[3] = INT2OID;            /* jointype */
306         typeId[4] = INTERNALOID;        /* SpecialJoinInfo */
307
308         /*
309          * As of Postgres 8.4, the preferred signature for join estimators has 5
310          * arguments, but we still allow the old 4-argument form. Try the
311          * preferred form first.
312          */
313         joinOid = LookupFuncName(joinName, 5, typeId, true);
314         if (!OidIsValid(joinOid))
315                 joinOid = LookupFuncName(joinName, 4, typeId, true);
316         /* If not found, reference the 5-argument signature in error msg */
317         if (!OidIsValid(joinOid))
318                 joinOid = LookupFuncName(joinName, 5, typeId, false);
319
320         /* estimators must return float8 */
321         if (get_func_rettype(joinOid) != FLOAT8OID)
322                 ereport(ERROR,
323                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
324                          errmsg("join estimator function %s must return type \"float8\"",
325                                         NameListToString(joinName))));
326
327         /* Require EXECUTE rights for the estimator */
328         aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
329         if (aclresult != ACLCHECK_OK)
330                 aclcheck_error(aclresult, ACL_KIND_PROC,
331                                            NameListToString(joinName));
332
333         return joinOid;
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 = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
348         if (!HeapTupleIsValid(tup)) /* should not happen */
349                 elog(ERROR, "cache lookup failed for operator %u", operOid);
350
351         simple_heap_delete(relation, &tup->t_self);
352
353         ReleaseSysCache(tup);
354
355         heap_close(relation, RowExclusiveLock);
356 }
357
358 /*
359  * AlterOperator
360  *              routine implementing ALTER OPERATOR <operator> SET (option = ...).
361  *
362  * Currently, only RESTRICT and JOIN estimator functions can be changed.
363  */
364 ObjectAddress
365 AlterOperator(AlterOperatorStmt *stmt)
366 {
367         ObjectAddress address;
368         Oid                     oprId;
369         Relation        catalog;
370         HeapTuple       tup;
371         Form_pg_operator oprForm;
372         int                     i;
373         ListCell   *pl;
374         Datum           values[Natts_pg_operator];
375         bool            nulls[Natts_pg_operator];
376         bool            replaces[Natts_pg_operator];
377         List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
378         bool            updateRestriction = false;
379         Oid                     restrictionOid;
380         List       *joinName = NIL; /* optional join sel. procedure */
381         bool            updateJoin = false;
382         Oid                     joinOid;
383
384         /* Look up the operator */
385         oprId = LookupOperNameTypeNames(NULL, stmt->opername,
386                                                                         (TypeName *) linitial(stmt->operargs),
387                                                                         (TypeName *) lsecond(stmt->operargs),
388                                                                         false, -1);
389         catalog = heap_open(OperatorRelationId, RowExclusiveLock);
390         tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
391         if (tup == NULL)
392                 elog(ERROR, "cache lookup failed for operator %u", oprId);
393         oprForm = (Form_pg_operator) GETSTRUCT(tup);
394
395         /* Process options */
396         foreach(pl, stmt->options)
397         {
398                 DefElem    *defel = (DefElem *) lfirst(pl);
399                 List       *param;
400
401                 if (defel->arg == NULL)
402                         param = NIL;            /* NONE, removes the function */
403                 else
404                         param = defGetQualifiedName(defel);
405
406                 if (pg_strcasecmp(defel->defname, "restrict") == 0)
407                 {
408                         restrictionName = param;
409                         updateRestriction = true;
410                 }
411                 else if (pg_strcasecmp(defel->defname, "join") == 0)
412                 {
413                         joinName = param;
414                         updateJoin = true;
415                 }
416
417                 /*
418                  * The rest of the options that CREATE accepts cannot be changed.
419                  * Check for them so that we can give a meaningful error message.
420                  */
421                 else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
422                                  pg_strcasecmp(defel->defname, "rightarg") == 0 ||
423                                  pg_strcasecmp(defel->defname, "procedure") == 0 ||
424                                  pg_strcasecmp(defel->defname, "commutator") == 0 ||
425                                  pg_strcasecmp(defel->defname, "negator") == 0 ||
426                                  pg_strcasecmp(defel->defname, "hashes") == 0 ||
427                                  pg_strcasecmp(defel->defname, "merges") == 0)
428                 {
429                         ereport(ERROR,
430                                         (errcode(ERRCODE_SYNTAX_ERROR),
431                                          errmsg("operator attribute \"%s\" can not be changed",
432                                                         defel->defname)));
433                 }
434                 else
435                         ereport(ERROR,
436                                         (errcode(ERRCODE_SYNTAX_ERROR),
437                                          errmsg("operator attribute \"%s\" not recognized",
438                                                         defel->defname)));
439         }
440
441         /* Check permissions. Must be owner. */
442         if (!pg_oper_ownercheck(oprId, GetUserId()))
443                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
444                                            NameStr(oprForm->oprname));
445
446         /*
447          * Look up restriction and join estimators if specified
448          */
449         if (restrictionName)
450                 restrictionOid = ValidateRestrictionEstimator(restrictionName);
451         else
452                 restrictionOid = InvalidOid;
453         if (joinName)
454                 joinOid = ValidateJoinEstimator(joinName);
455         else
456                 joinOid = InvalidOid;
457
458         /* Perform additional checks, like OperatorCreate does */
459         if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
460         {
461                 /* If it's not a binary op, these things mustn't be set: */
462                 if (OidIsValid(joinOid))
463                         ereport(ERROR,
464                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
465                                  errmsg("only binary operators can have join selectivity")));
466         }
467
468         if (oprForm->oprresult != BOOLOID)
469         {
470                 if (OidIsValid(restrictionOid))
471                         ereport(ERROR,
472                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
473                                          errmsg("only boolean operators can have restriction selectivity")));
474                 if (OidIsValid(joinOid))
475                         ereport(ERROR,
476                                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
477                                 errmsg("only boolean operators can have join selectivity")));
478         }
479
480         /* Update the tuple */
481         for (i = 0; i < Natts_pg_operator; ++i)
482         {
483                 values[i] = (Datum) 0;
484                 replaces[i] = false;
485                 nulls[i] = false;
486         }
487         if (updateRestriction)
488         {
489                 replaces[Anum_pg_operator_oprrest - 1] = true;
490                 values[Anum_pg_operator_oprrest - 1] = restrictionOid;
491         }
492         if (updateJoin)
493         {
494                 replaces[Anum_pg_operator_oprjoin - 1] = true;
495                 values[Anum_pg_operator_oprjoin - 1] = joinOid;
496         }
497
498         tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
499                                                         values, nulls, replaces);
500
501         simple_heap_update(catalog, &tup->t_self, tup);
502         CatalogUpdateIndexes(catalog, tup);
503
504         address = makeOperatorDependencies(tup, true);
505
506         InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
507
508         heap_close(catalog, NoLock);
509
510         return address;
511 }