1 /*-------------------------------------------------------------------------
2 *
3 * operatorcmds.c
4 *
5 * Routines for operator manipulation commands
6 *
7 * Portions Copyright (c) 1996-2016, 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
DefineOperator(List * names,List * parameters)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
ValidateRestrictionEstimator(List * restrictionName)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 %s",
279 NameListToString(restrictionName), "float8")));
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
ValidateJoinEstimator(List * joinName)296 ValidateJoinEstimator(List *joinName)
297 {
298 Oid typeId[5];
299 Oid joinOid;
300 Oid joinOid2;
301 AclResult aclresult;
302
303 typeId[0] = INTERNALOID; /* PlannerInfo */
304 typeId[1] = OIDOID; /* operator OID */
305 typeId[2] = INTERNALOID; /* args list */
306 typeId[3] = INT2OID; /* jointype */
307 typeId[4] = INTERNALOID; /* SpecialJoinInfo */
308
309 /*
310 * As of Postgres 8.4, the preferred signature for join estimators has 5
311 * arguments, but we still allow the old 4-argument form. Whine about
312 * ambiguity if both forms exist.
313 */
314 joinOid = LookupFuncName(joinName, 5, typeId, true);
315 joinOid2 = LookupFuncName(joinName, 4, typeId, true);
316 if (OidIsValid(joinOid))
317 {
318 if (OidIsValid(joinOid2))
319 ereport(ERROR,
320 (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
321 errmsg("join estimator function %s has multiple matches",
322 NameListToString(joinName))));
323 }
324 else
325 {
326 joinOid = joinOid2;
327 /* If not found, reference the 5-argument signature in error msg */
328 if (!OidIsValid(joinOid))
329 joinOid = LookupFuncName(joinName, 5, typeId, false);
330 }
331
332 /* estimators must return float8 */
333 if (get_func_rettype(joinOid) != FLOAT8OID)
334 ereport(ERROR,
335 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
336 errmsg("join estimator function %s must return type %s",
337 NameListToString(joinName), "float8")));
338
339 /* Require EXECUTE rights for the estimator */
340 aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
341 if (aclresult != ACLCHECK_OK)
342 aclcheck_error(aclresult, ACL_KIND_PROC,
343 NameListToString(joinName));
344
345 return joinOid;
346 }
347
348 /*
349 * Guts of operator deletion.
350 */
351 void
RemoveOperatorById(Oid operOid)352 RemoveOperatorById(Oid operOid)
353 {
354 Relation relation;
355 HeapTuple tup;
356 Form_pg_operator op;
357
358 relation = heap_open(OperatorRelationId, RowExclusiveLock);
359
360 tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
361 if (!HeapTupleIsValid(tup)) /* should not happen */
362 elog(ERROR, "cache lookup failed for operator %u", operOid);
363 op = (Form_pg_operator) GETSTRUCT(tup);
364
365 /*
366 * Reset links from commutator and negator, if any. In case of a
367 * self-commutator or self-negator, this means we have to re-fetch the
368 * updated tuple. (We could optimize away updates on the tuple we're
369 * about to drop, but it doesn't seem worth convoluting the logic for.)
370 */
371 if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
372 {
373 OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
374 if (operOid == op->oprcom || operOid == op->oprnegate)
375 {
376 ReleaseSysCache(tup);
377 tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
378 if (!HeapTupleIsValid(tup)) /* should not happen */
379 elog(ERROR, "cache lookup failed for operator %u", operOid);
380 }
381 }
382
383 simple_heap_delete(relation, &tup->t_self);
384
385 ReleaseSysCache(tup);
386
387 heap_close(relation, RowExclusiveLock);
388 }
389
390 /*
391 * AlterOperator
392 * routine implementing ALTER OPERATOR <operator> SET (option = ...).
393 *
394 * Currently, only RESTRICT and JOIN estimator functions can be changed.
395 */
396 ObjectAddress
AlterOperator(AlterOperatorStmt * stmt)397 AlterOperator(AlterOperatorStmt *stmt)
398 {
399 ObjectAddress address;
400 Oid oprId;
401 Relation catalog;
402 HeapTuple tup;
403 Form_pg_operator oprForm;
404 int i;
405 ListCell *pl;
406 Datum values[Natts_pg_operator];
407 bool nulls[Natts_pg_operator];
408 bool replaces[Natts_pg_operator];
409 List *restrictionName = NIL; /* optional restrict. sel. procedure */
410 bool updateRestriction = false;
411 Oid restrictionOid;
412 List *joinName = NIL; /* optional join sel. procedure */
413 bool updateJoin = false;
414 Oid joinOid;
415
416 /* Look up the operator */
417 oprId = LookupOperNameTypeNames(NULL, stmt->opername,
418 (TypeName *) linitial(stmt->operargs),
419 (TypeName *) lsecond(stmt->operargs),
420 false, -1);
421 catalog = heap_open(OperatorRelationId, RowExclusiveLock);
422 tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
423 if (tup == NULL)
424 elog(ERROR, "cache lookup failed for operator %u", oprId);
425 oprForm = (Form_pg_operator) GETSTRUCT(tup);
426
427 /* Process options */
428 foreach(pl, stmt->options)
429 {
430 DefElem *defel = (DefElem *) lfirst(pl);
431 List *param;
432
433 if (defel->arg == NULL)
434 param = NIL; /* NONE, removes the function */
435 else
436 param = defGetQualifiedName(defel);
437
438 if (pg_strcasecmp(defel->defname, "restrict") == 0)
439 {
440 restrictionName = param;
441 updateRestriction = true;
442 }
443 else if (pg_strcasecmp(defel->defname, "join") == 0)
444 {
445 joinName = param;
446 updateJoin = true;
447 }
448
449 /*
450 * The rest of the options that CREATE accepts cannot be changed.
451 * Check for them so that we can give a meaningful error message.
452 */
453 else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
454 pg_strcasecmp(defel->defname, "rightarg") == 0 ||
455 pg_strcasecmp(defel->defname, "procedure") == 0 ||
456 pg_strcasecmp(defel->defname, "commutator") == 0 ||
457 pg_strcasecmp(defel->defname, "negator") == 0 ||
458 pg_strcasecmp(defel->defname, "hashes") == 0 ||
459 pg_strcasecmp(defel->defname, "merges") == 0)
460 {
461 ereport(ERROR,
462 (errcode(ERRCODE_SYNTAX_ERROR),
463 errmsg("operator attribute \"%s\" cannot be changed",
464 defel->defname)));
465 }
466 else
467 ereport(ERROR,
468 (errcode(ERRCODE_SYNTAX_ERROR),
469 errmsg("operator attribute \"%s\" not recognized",
470 defel->defname)));
471 }
472
473 /* Check permissions. Must be owner. */
474 if (!pg_oper_ownercheck(oprId, GetUserId()))
475 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
476 NameStr(oprForm->oprname));
477
478 /*
479 * Look up restriction and join estimators if specified
480 */
481 if (restrictionName)
482 restrictionOid = ValidateRestrictionEstimator(restrictionName);
483 else
484 restrictionOid = InvalidOid;
485 if (joinName)
486 joinOid = ValidateJoinEstimator(joinName);
487 else
488 joinOid = InvalidOid;
489
490 /* Perform additional checks, like OperatorCreate does */
491 if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
492 {
493 /* If it's not a binary op, these things mustn't be set: */
494 if (OidIsValid(joinOid))
495 ereport(ERROR,
496 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
497 errmsg("only binary operators can have join selectivity")));
498 }
499
500 if (oprForm->oprresult != BOOLOID)
501 {
502 if (OidIsValid(restrictionOid))
503 ereport(ERROR,
504 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
505 errmsg("only boolean operators can have restriction selectivity")));
506 if (OidIsValid(joinOid))
507 ereport(ERROR,
508 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
509 errmsg("only boolean operators can have join selectivity")));
510 }
511
512 /* Update the tuple */
513 for (i = 0; i < Natts_pg_operator; ++i)
514 {
515 values[i] = (Datum) 0;
516 replaces[i] = false;
517 nulls[i] = false;
518 }
519 if (updateRestriction)
520 {
521 replaces[Anum_pg_operator_oprrest - 1] = true;
522 values[Anum_pg_operator_oprrest - 1] = restrictionOid;
523 }
524 if (updateJoin)
525 {
526 replaces[Anum_pg_operator_oprjoin - 1] = true;
527 values[Anum_pg_operator_oprjoin - 1] = joinOid;
528 }
529
530 tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
531 values, nulls, replaces);
532
533 simple_heap_update(catalog, &tup->t_self, tup);
534 CatalogUpdateIndexes(catalog, tup);
535
536 address = makeOperatorDependencies(tup, true);
537
538 InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
539
540 heap_close(catalog, NoLock);
541
542 return address;
543 }
544