1 /*-------------------------------------------------------------------------
2 *
3 * operatorcmds.c
4 *
5 * Routines for operator manipulation commands
6 *
7 * Portions Copyright (c) 1996-2021, 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 functions
25 * "create type":
26 * type
27 * "create operator":
28 * operators
29 *
30 *-------------------------------------------------------------------------
31 */
32 #include "postgres.h"
33
34 #include "access/htup_details.h"
35 #include "access/table.h"
36 #include "catalog/dependency.h"
37 #include "catalog/indexing.h"
38 #include "catalog/objectaccess.h"
39 #include "catalog/pg_operator.h"
40 #include "catalog/pg_type.h"
41 #include "commands/alter.h"
42 #include "commands/defrem.h"
43 #include "miscadmin.h"
44 #include "parser/parse_func.h"
45 #include "parser/parse_oper.h"
46 #include "parser/parse_type.h"
47 #include "utils/acl.h"
48 #include "utils/builtins.h"
49 #include "utils/lsyscache.h"
50 #include "utils/rel.h"
51 #include "utils/syscache.h"
52
53 static Oid ValidateRestrictionEstimator(List *restrictionName);
54 static Oid ValidateJoinEstimator(List *joinName);
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 ObjectAddress
DefineOperator(List * names,List * parameters)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. function */
81 List *joinName = NIL; /* optional join sel. function */
82 Oid functionOid; /* functions converted to OID */
83 Oid restrictionOid;
84 Oid joinOid;
85 Oid typeId[2]; /* to hold left and right arg */
86 int nargs;
87 ListCell *pl;
88
89 /* Convert list of names to a name and namespace */
90 oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
91
92 /* Check we have creation rights in target namespace */
93 aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
94 if (aclresult != ACLCHECK_OK)
95 aclcheck_error(aclresult, OBJECT_SCHEMA,
96 get_namespace_name(oprNamespace));
97
98 /*
99 * loop over the definition list and extract the information we need.
100 */
101 foreach(pl, parameters)
102 {
103 DefElem *defel = (DefElem *) lfirst(pl);
104
105 if (strcmp(defel->defname, "leftarg") == 0)
106 {
107 typeName1 = defGetTypeName(defel);
108 if (typeName1->setof)
109 ereport(ERROR,
110 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
111 errmsg("SETOF type not allowed for operator argument")));
112 }
113 else if (strcmp(defel->defname, "rightarg") == 0)
114 {
115 typeName2 = defGetTypeName(defel);
116 if (typeName2->setof)
117 ereport(ERROR,
118 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
119 errmsg("SETOF type not allowed for operator argument")));
120 }
121 /* "function" and "procedure" are equivalent here */
122 else if (strcmp(defel->defname, "function") == 0)
123 functionName = defGetQualifiedName(defel);
124 else if (strcmp(defel->defname, "procedure") == 0)
125 functionName = defGetQualifiedName(defel);
126 else if (strcmp(defel->defname, "commutator") == 0)
127 commutatorName = defGetQualifiedName(defel);
128 else if (strcmp(defel->defname, "negator") == 0)
129 negatorName = defGetQualifiedName(defel);
130 else if (strcmp(defel->defname, "restrict") == 0)
131 restrictionName = defGetQualifiedName(defel);
132 else if (strcmp(defel->defname, "join") == 0)
133 joinName = defGetQualifiedName(defel);
134 else if (strcmp(defel->defname, "hashes") == 0)
135 canHash = defGetBoolean(defel);
136 else if (strcmp(defel->defname, "merges") == 0)
137 canMerge = defGetBoolean(defel);
138 /* These obsolete options are taken as meaning canMerge */
139 else if (strcmp(defel->defname, "sort1") == 0)
140 canMerge = true;
141 else if (strcmp(defel->defname, "sort2") == 0)
142 canMerge = true;
143 else if (strcmp(defel->defname, "ltcmp") == 0)
144 canMerge = true;
145 else if (strcmp(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 function 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 /*
172 * If only the right argument is missing, the user is likely trying to
173 * create a postfix operator, so give them a hint about why that does not
174 * work. But if both arguments are missing, do not mention postfix
175 * operators, as the user most likely simply neglected to mention the
176 * arguments.
177 */
178 if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
179 ereport(ERROR,
180 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
181 errmsg("operator argument types must be specified")));
182 if (!OidIsValid(typeId2))
183 ereport(ERROR,
184 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
185 errmsg("operator right argument type must be specified"),
186 errdetail("Postfix operators are not supported.")));
187
188 if (typeName1)
189 {
190 aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
191 if (aclresult != ACLCHECK_OK)
192 aclcheck_error_type(aclresult, typeId1);
193 }
194
195 if (typeName2)
196 {
197 aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
198 if (aclresult != ACLCHECK_OK)
199 aclcheck_error_type(aclresult, typeId2);
200 }
201
202 /*
203 * Look up the operator's underlying function.
204 */
205 if (!OidIsValid(typeId1))
206 {
207 typeId[0] = typeId2;
208 nargs = 1;
209 }
210 else if (!OidIsValid(typeId2))
211 {
212 typeId[0] = typeId1;
213 nargs = 1;
214 }
215 else
216 {
217 typeId[0] = typeId1;
218 typeId[1] = typeId2;
219 nargs = 2;
220 }
221 functionOid = LookupFuncName(functionName, nargs, typeId, false);
222
223 /*
224 * We require EXECUTE rights for the function. This isn't strictly
225 * necessary, since EXECUTE will be checked at any attempted use of the
226 * operator, but it seems like a good idea anyway.
227 */
228 aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
229 if (aclresult != ACLCHECK_OK)
230 aclcheck_error(aclresult, OBJECT_FUNCTION,
231 NameListToString(functionName));
232
233 rettype = get_func_rettype(functionOid);
234 aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
235 if (aclresult != ACLCHECK_OK)
236 aclcheck_error_type(aclresult, rettype);
237
238 /*
239 * Look up restriction and join estimators if specified
240 */
241 if (restrictionName)
242 restrictionOid = ValidateRestrictionEstimator(restrictionName);
243 else
244 restrictionOid = InvalidOid;
245 if (joinName)
246 joinOid = ValidateJoinEstimator(joinName);
247 else
248 joinOid = InvalidOid;
249
250 /*
251 * now have OperatorCreate do all the work..
252 */
253 return
254 OperatorCreate(oprName, /* operator name */
255 oprNamespace, /* namespace */
256 typeId1, /* left type id */
257 typeId2, /* right type id */
258 functionOid, /* function for operator */
259 commutatorName, /* optional commutator operator name */
260 negatorName, /* optional negator operator name */
261 restrictionOid, /* optional restrict. sel. function */
262 joinOid, /* optional join sel. function name */
263 canMerge, /* operator merges */
264 canHash); /* operator hashes */
265 }
266
267 /*
268 * Look up a restriction estimator function by name, and verify that it has
269 * the correct signature and we have the permissions to attach it to an
270 * operator.
271 */
272 static Oid
ValidateRestrictionEstimator(List * restrictionName)273 ValidateRestrictionEstimator(List *restrictionName)
274 {
275 Oid typeId[4];
276 Oid restrictionOid;
277 AclResult aclresult;
278
279 typeId[0] = INTERNALOID; /* PlannerInfo */
280 typeId[1] = OIDOID; /* operator OID */
281 typeId[2] = INTERNALOID; /* args list */
282 typeId[3] = INT4OID; /* varRelid */
283
284 restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
285
286 /* estimators must return float8 */
287 if (get_func_rettype(restrictionOid) != FLOAT8OID)
288 ereport(ERROR,
289 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
290 errmsg("restriction estimator function %s must return type %s",
291 NameListToString(restrictionName), "float8")));
292
293 /* Require EXECUTE rights for the estimator */
294 aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
295 if (aclresult != ACLCHECK_OK)
296 aclcheck_error(aclresult, OBJECT_FUNCTION,
297 NameListToString(restrictionName));
298
299 return restrictionOid;
300 }
301
302 /*
303 * Look up a join estimator function by name, and verify that it has the
304 * correct signature and we have the permissions to attach it to an
305 * operator.
306 */
307 static Oid
ValidateJoinEstimator(List * joinName)308 ValidateJoinEstimator(List *joinName)
309 {
310 Oid typeId[5];
311 Oid joinOid;
312 Oid joinOid2;
313 AclResult aclresult;
314
315 typeId[0] = INTERNALOID; /* PlannerInfo */
316 typeId[1] = OIDOID; /* operator OID */
317 typeId[2] = INTERNALOID; /* args list */
318 typeId[3] = INT2OID; /* jointype */
319 typeId[4] = INTERNALOID; /* SpecialJoinInfo */
320
321 /*
322 * As of Postgres 8.4, the preferred signature for join estimators has 5
323 * arguments, but we still allow the old 4-argument form. Whine about
324 * ambiguity if both forms exist.
325 */
326 joinOid = LookupFuncName(joinName, 5, typeId, true);
327 joinOid2 = LookupFuncName(joinName, 4, typeId, true);
328 if (OidIsValid(joinOid))
329 {
330 if (OidIsValid(joinOid2))
331 ereport(ERROR,
332 (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
333 errmsg("join estimator function %s has multiple matches",
334 NameListToString(joinName))));
335 }
336 else
337 {
338 joinOid = joinOid2;
339 /* If not found, reference the 5-argument signature in error msg */
340 if (!OidIsValid(joinOid))
341 joinOid = LookupFuncName(joinName, 5, typeId, false);
342 }
343
344 /* estimators must return float8 */
345 if (get_func_rettype(joinOid) != FLOAT8OID)
346 ereport(ERROR,
347 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
348 errmsg("join estimator function %s must return type %s",
349 NameListToString(joinName), "float8")));
350
351 /* Require EXECUTE rights for the estimator */
352 aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
353 if (aclresult != ACLCHECK_OK)
354 aclcheck_error(aclresult, OBJECT_FUNCTION,
355 NameListToString(joinName));
356
357 return joinOid;
358 }
359
360 /*
361 * Guts of operator deletion.
362 */
363 void
RemoveOperatorById(Oid operOid)364 RemoveOperatorById(Oid operOid)
365 {
366 Relation relation;
367 HeapTuple tup;
368 Form_pg_operator op;
369
370 relation = table_open(OperatorRelationId, RowExclusiveLock);
371
372 tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
373 if (!HeapTupleIsValid(tup)) /* should not happen */
374 elog(ERROR, "cache lookup failed for operator %u", operOid);
375 op = (Form_pg_operator) GETSTRUCT(tup);
376
377 /*
378 * Reset links from commutator and negator, if any. In case of a
379 * self-commutator or self-negator, this means we have to re-fetch the
380 * updated tuple. (We could optimize away updates on the tuple we're
381 * about to drop, but it doesn't seem worth convoluting the logic for.)
382 */
383 if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
384 {
385 OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
386 if (operOid == op->oprcom || operOid == op->oprnegate)
387 {
388 ReleaseSysCache(tup);
389 tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
390 if (!HeapTupleIsValid(tup)) /* should not happen */
391 elog(ERROR, "cache lookup failed for operator %u", operOid);
392 }
393 }
394
395 CatalogTupleDelete(relation, &tup->t_self);
396
397 ReleaseSysCache(tup);
398
399 table_close(relation, RowExclusiveLock);
400 }
401
402 /*
403 * AlterOperator
404 * routine implementing ALTER OPERATOR <operator> SET (option = ...).
405 *
406 * Currently, only RESTRICT and JOIN estimator functions can be changed.
407 */
408 ObjectAddress
AlterOperator(AlterOperatorStmt * stmt)409 AlterOperator(AlterOperatorStmt *stmt)
410 {
411 ObjectAddress address;
412 Oid oprId;
413 Relation catalog;
414 HeapTuple tup;
415 Form_pg_operator oprForm;
416 int i;
417 ListCell *pl;
418 Datum values[Natts_pg_operator];
419 bool nulls[Natts_pg_operator];
420 bool replaces[Natts_pg_operator];
421 List *restrictionName = NIL; /* optional restrict. sel. function */
422 bool updateRestriction = false;
423 Oid restrictionOid;
424 List *joinName = NIL; /* optional join sel. function */
425 bool updateJoin = false;
426 Oid joinOid;
427
428 /* Look up the operator */
429 oprId = LookupOperWithArgs(stmt->opername, false);
430 catalog = table_open(OperatorRelationId, RowExclusiveLock);
431 tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
432 if (!HeapTupleIsValid(tup))
433 elog(ERROR, "cache lookup failed for operator %u", oprId);
434 oprForm = (Form_pg_operator) GETSTRUCT(tup);
435
436 /* Process options */
437 foreach(pl, stmt->options)
438 {
439 DefElem *defel = (DefElem *) lfirst(pl);
440 List *param;
441
442 if (defel->arg == NULL)
443 param = NIL; /* NONE, removes the function */
444 else
445 param = defGetQualifiedName(defel);
446
447 if (strcmp(defel->defname, "restrict") == 0)
448 {
449 restrictionName = param;
450 updateRestriction = true;
451 }
452 else if (strcmp(defel->defname, "join") == 0)
453 {
454 joinName = param;
455 updateJoin = true;
456 }
457
458 /*
459 * The rest of the options that CREATE accepts cannot be changed.
460 * Check for them so that we can give a meaningful error message.
461 */
462 else if (strcmp(defel->defname, "leftarg") == 0 ||
463 strcmp(defel->defname, "rightarg") == 0 ||
464 strcmp(defel->defname, "function") == 0 ||
465 strcmp(defel->defname, "procedure") == 0 ||
466 strcmp(defel->defname, "commutator") == 0 ||
467 strcmp(defel->defname, "negator") == 0 ||
468 strcmp(defel->defname, "hashes") == 0 ||
469 strcmp(defel->defname, "merges") == 0)
470 {
471 ereport(ERROR,
472 (errcode(ERRCODE_SYNTAX_ERROR),
473 errmsg("operator attribute \"%s\" cannot be changed",
474 defel->defname)));
475 }
476 else
477 ereport(ERROR,
478 (errcode(ERRCODE_SYNTAX_ERROR),
479 errmsg("operator attribute \"%s\" not recognized",
480 defel->defname)));
481 }
482
483 /* Check permissions. Must be owner. */
484 if (!pg_oper_ownercheck(oprId, GetUserId()))
485 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
486 NameStr(oprForm->oprname));
487
488 /*
489 * Look up restriction and join estimators if specified
490 */
491 if (restrictionName)
492 restrictionOid = ValidateRestrictionEstimator(restrictionName);
493 else
494 restrictionOid = InvalidOid;
495 if (joinName)
496 joinOid = ValidateJoinEstimator(joinName);
497 else
498 joinOid = InvalidOid;
499
500 /* Perform additional checks, like OperatorCreate does */
501 if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
502 {
503 /* If it's not a binary op, these things mustn't be set: */
504 if (OidIsValid(joinOid))
505 ereport(ERROR,
506 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
507 errmsg("only binary operators can have join selectivity")));
508 }
509
510 if (oprForm->oprresult != BOOLOID)
511 {
512 if (OidIsValid(restrictionOid))
513 ereport(ERROR,
514 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
515 errmsg("only boolean operators can have restriction selectivity")));
516 if (OidIsValid(joinOid))
517 ereport(ERROR,
518 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
519 errmsg("only boolean operators can have join selectivity")));
520 }
521
522 /* Update the tuple */
523 for (i = 0; i < Natts_pg_operator; ++i)
524 {
525 values[i] = (Datum) 0;
526 replaces[i] = false;
527 nulls[i] = false;
528 }
529 if (updateRestriction)
530 {
531 replaces[Anum_pg_operator_oprrest - 1] = true;
532 values[Anum_pg_operator_oprrest - 1] = restrictionOid;
533 }
534 if (updateJoin)
535 {
536 replaces[Anum_pg_operator_oprjoin - 1] = true;
537 values[Anum_pg_operator_oprjoin - 1] = joinOid;
538 }
539
540 tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
541 values, nulls, replaces);
542
543 CatalogTupleUpdate(catalog, &tup->t_self, tup);
544
545 address = makeOperatorDependencies(tup, false, true);
546
547 InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
548
549 table_close(catalog, NoLock);
550
551 return address;
552 }
553