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