Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * operatorcmds.c
4 : *
5 : * Routines for operator manipulation commands
6 : *
7 : * Portions Copyright (c) 1996-2024, 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/indexing.h"
37 : #include "catalog/objectaccess.h"
38 : #include "catalog/pg_namespace.h"
39 : #include "catalog/pg_operator.h"
40 : #include "catalog/pg_proc.h"
41 : #include "catalog/pg_type.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/lsyscache.h"
49 : #include "utils/rel.h"
50 : #include "utils/syscache.h"
51 :
52 : static Oid ValidateRestrictionEstimator(List *restrictionName);
53 : static Oid ValidateJoinEstimator(List *joinName);
54 : static Oid ValidateOperatorReference(List *name,
55 : Oid leftTypeId,
56 : Oid rightTypeId);
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 1578 : DefineOperator(List *names, List *parameters)
68 : {
69 : char *oprName;
70 : Oid oprNamespace;
71 : AclResult aclresult;
72 1578 : bool canMerge = false; /* operator merges */
73 1578 : bool canHash = false; /* operator hashes */
74 1578 : List *functionName = NIL; /* function for operator */
75 1578 : TypeName *typeName1 = NULL; /* first type name */
76 1578 : TypeName *typeName2 = NULL; /* second type name */
77 1578 : Oid typeId1 = InvalidOid; /* types converted to OID */
78 1578 : Oid typeId2 = InvalidOid;
79 : Oid rettype;
80 1578 : List *commutatorName = NIL; /* optional commutator operator name */
81 1578 : List *negatorName = NIL; /* optional negator operator name */
82 1578 : List *restrictionName = NIL; /* optional restrict. sel. function */
83 1578 : 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 1578 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 :
94 : /* Check we have creation rights in target namespace */
95 1578 : aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
96 1578 : 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 10138 : foreach(pl, parameters)
104 : {
105 8578 : DefElem *defel = (DefElem *) lfirst(pl);
106 :
107 8578 : if (strcmp(defel->defname, "leftarg") == 0)
108 : {
109 1480 : typeName1 = defGetTypeName(defel);
110 1480 : if (typeName1->setof)
111 6 : ereport(ERROR,
112 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
113 : errmsg("SETOF type not allowed for operator argument")));
114 : }
115 7098 : else if (strcmp(defel->defname, "rightarg") == 0)
116 : {
117 1548 : typeName2 = defGetTypeName(defel);
118 1548 : 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 5550 : else if (strcmp(defel->defname, "function") == 0)
125 28 : functionName = defGetQualifiedName(defel);
126 5522 : else if (strcmp(defel->defname, "procedure") == 0)
127 1520 : functionName = defGetQualifiedName(defel);
128 4002 : else if (strcmp(defel->defname, "commutator") == 0)
129 986 : commutatorName = defGetQualifiedName(defel);
130 3016 : else if (strcmp(defel->defname, "negator") == 0)
131 670 : 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 1560 : 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 1548 : if (typeName1)
169 1474 : typeId1 = typenameTypeId(NULL, typeName1);
170 1548 : if (typeName2)
171 1536 : 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 1548 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
181 6 : ereport(ERROR,
182 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
183 : errmsg("operator argument types must be specified")));
184 1542 : 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 1536 : if (typeName1)
191 : {
192 1468 : aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
193 1468 : if (aclresult != ACLCHECK_OK)
194 12 : aclcheck_error_type(aclresult, typeId1);
195 : }
196 :
197 1524 : if (typeName2)
198 : {
199 1524 : aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
200 1524 : if (aclresult != ACLCHECK_OK)
201 6 : aclcheck_error_type(aclresult, typeId2);
202 : }
203 :
204 : /*
205 : * Look up the operator's underlying function.
206 : */
207 1518 : if (!OidIsValid(typeId1))
208 : {
209 68 : typeId[0] = typeId2;
210 68 : nargs = 1;
211 : }
212 1450 : else if (!OidIsValid(typeId2))
213 : {
214 0 : typeId[0] = typeId1;
215 0 : nargs = 1;
216 : }
217 : else
218 : {
219 1450 : typeId[0] = typeId1;
220 1450 : typeId[1] = typeId2;
221 1450 : nargs = 2;
222 : }
223 1518 : 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 1518 : aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
231 1518 : if (aclresult != ACLCHECK_OK)
232 6 : aclcheck_error(aclresult, OBJECT_FUNCTION,
233 6 : NameListToString(functionName));
234 :
235 1512 : rettype = get_func_rettype(functionOid);
236 1512 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
237 1512 : if (aclresult != ACLCHECK_OK)
238 6 : aclcheck_error_type(aclresult, rettype);
239 :
240 : /*
241 : * Look up restriction and join estimators if specified
242 : */
243 1506 : if (restrictionName)
244 1022 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
245 : else
246 484 : restrictionOid = InvalidOid;
247 1506 : if (joinName)
248 998 : joinOid = ValidateJoinEstimator(joinName);
249 : else
250 508 : joinOid = InvalidOid;
251 :
252 : /*
253 : * now have OperatorCreate do all the work..
254 : */
255 : return
256 1506 : 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 : * Look up and return the OID of an operator,
364 : * given a possibly-qualified name and left and right type IDs.
365 : *
366 : * Verifies that the operator is defined (not a shell) and owned by
367 : * the current user, so that we have permission to associate it with
368 : * the operator being altered. Rejecting shell operators is a policy
369 : * choice to help catch mistakes, rather than something essential.
370 : */
371 : static Oid
372 54 : ValidateOperatorReference(List *name,
373 : Oid leftTypeId,
374 : Oid rightTypeId)
375 : {
376 : Oid oid;
377 : bool defined;
378 :
379 54 : oid = OperatorLookup(name,
380 : leftTypeId,
381 : rightTypeId,
382 : &defined);
383 :
384 : /* These message strings are chosen to match parse_oper.c */
385 54 : if (!OidIsValid(oid))
386 0 : ereport(ERROR,
387 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
388 : errmsg("operator does not exist: %s",
389 : op_signature_string(name,
390 : leftTypeId,
391 : rightTypeId))));
392 :
393 54 : if (!defined)
394 0 : ereport(ERROR,
395 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
396 : errmsg("operator is only a shell: %s",
397 : op_signature_string(name,
398 : leftTypeId,
399 : rightTypeId))));
400 :
401 54 : if (!object_ownercheck(OperatorRelationId, oid, GetUserId()))
402 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
403 0 : NameListToString(name));
404 :
405 54 : return oid;
406 : }
407 :
408 :
409 : /*
410 : * Guts of operator deletion.
411 : */
412 : void
413 734 : RemoveOperatorById(Oid operOid)
414 : {
415 : Relation relation;
416 : HeapTuple tup;
417 : Form_pg_operator op;
418 :
419 734 : relation = table_open(OperatorRelationId, RowExclusiveLock);
420 :
421 734 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
422 734 : if (!HeapTupleIsValid(tup)) /* should not happen */
423 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
424 734 : op = (Form_pg_operator) GETSTRUCT(tup);
425 :
426 : /*
427 : * Reset links from commutator and negator, if any. In case of a
428 : * self-commutator or self-negator, this means we have to re-fetch the
429 : * updated tuple. (We could optimize away updates on the tuple we're
430 : * about to drop, but it doesn't seem worth convoluting the logic for.)
431 : */
432 734 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
433 : {
434 330 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
435 330 : if (operOid == op->oprcom || operOid == op->oprnegate)
436 : {
437 100 : ReleaseSysCache(tup);
438 100 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
439 100 : if (!HeapTupleIsValid(tup)) /* should not happen */
440 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
441 : }
442 : }
443 :
444 734 : CatalogTupleDelete(relation, &tup->t_self);
445 :
446 734 : ReleaseSysCache(tup);
447 :
448 734 : table_close(relation, RowExclusiveLock);
449 734 : }
450 :
451 : /*
452 : * AlterOperator
453 : * routine implementing ALTER OPERATOR <operator> SET (option = ...).
454 : *
455 : * Currently, only RESTRICT and JOIN estimator functions can be changed.
456 : * COMMUTATOR, NEGATOR, MERGES, and HASHES attributes can be set if they
457 : * have not been set previously. (Changing or removing one of these
458 : * attributes could invalidate existing plans, which seems more trouble
459 : * than it's worth.)
460 : */
461 : ObjectAddress
462 608 : AlterOperator(AlterOperatorStmt *stmt)
463 : {
464 : ObjectAddress address;
465 : Oid oprId;
466 : Relation catalog;
467 : HeapTuple tup;
468 : Form_pg_operator oprForm;
469 : int i;
470 : ListCell *pl;
471 : Datum values[Natts_pg_operator];
472 : bool nulls[Natts_pg_operator];
473 : bool replaces[Natts_pg_operator];
474 608 : List *restrictionName = NIL; /* optional restrict. sel. function */
475 608 : bool updateRestriction = false;
476 : Oid restrictionOid;
477 608 : List *joinName = NIL; /* optional join sel. function */
478 608 : bool updateJoin = false;
479 : Oid joinOid;
480 608 : List *commutatorName = NIL; /* optional commutator operator name */
481 : Oid commutatorOid;
482 608 : List *negatorName = NIL; /* optional negator operator name */
483 : Oid negatorOid;
484 608 : bool canMerge = false;
485 608 : bool updateMerges = false;
486 608 : bool canHash = false;
487 608 : bool updateHashes = false;
488 :
489 : /* Look up the operator */
490 608 : oprId = LookupOperWithArgs(stmt->opername, false);
491 608 : catalog = table_open(OperatorRelationId, RowExclusiveLock);
492 608 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
493 608 : if (!HeapTupleIsValid(tup))
494 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
495 608 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
496 :
497 : /* Process options */
498 1658 : foreach(pl, stmt->options)
499 : {
500 1056 : DefElem *defel = (DefElem *) lfirst(pl);
501 : List *param;
502 :
503 1056 : if (defel->arg == NULL)
504 64 : param = NIL; /* NONE, removes the function */
505 : else
506 992 : param = defGetQualifiedName(defel);
507 :
508 1056 : if (strcmp(defel->defname, "restrict") == 0)
509 : {
510 472 : restrictionName = param;
511 472 : updateRestriction = true;
512 : }
513 584 : else if (strcmp(defel->defname, "join") == 0)
514 : {
515 466 : joinName = param;
516 466 : updateJoin = true;
517 : }
518 118 : else if (strcmp(defel->defname, "commutator") == 0)
519 : {
520 24 : commutatorName = defGetQualifiedName(defel);
521 : }
522 94 : else if (strcmp(defel->defname, "negator") == 0)
523 : {
524 30 : negatorName = defGetQualifiedName(defel);
525 : }
526 64 : else if (strcmp(defel->defname, "merges") == 0)
527 : {
528 24 : canMerge = defGetBoolean(defel);
529 24 : updateMerges = true;
530 : }
531 40 : else if (strcmp(defel->defname, "hashes") == 0)
532 : {
533 34 : canHash = defGetBoolean(defel);
534 34 : updateHashes = true;
535 : }
536 :
537 : /*
538 : * The rest of the options that CREATE accepts cannot be changed.
539 : * Check for them so that we can give a meaningful error message.
540 : */
541 6 : else if (strcmp(defel->defname, "leftarg") == 0 ||
542 6 : strcmp(defel->defname, "rightarg") == 0 ||
543 6 : strcmp(defel->defname, "function") == 0 ||
544 6 : strcmp(defel->defname, "procedure") == 0)
545 : {
546 0 : ereport(ERROR,
547 : (errcode(ERRCODE_SYNTAX_ERROR),
548 : errmsg("operator attribute \"%s\" cannot be changed",
549 : defel->defname)));
550 : }
551 : else
552 6 : ereport(ERROR,
553 : (errcode(ERRCODE_SYNTAX_ERROR),
554 : errmsg("operator attribute \"%s\" not recognized",
555 : defel->defname)));
556 : }
557 :
558 : /* Check permissions. Must be owner. */
559 602 : if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
560 6 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
561 6 : NameStr(oprForm->oprname));
562 :
563 : /*
564 : * Look up OIDs for any parameters specified
565 : */
566 596 : if (restrictionName)
567 454 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
568 : else
569 142 : restrictionOid = InvalidOid;
570 590 : if (joinName)
571 454 : joinOid = ValidateJoinEstimator(joinName);
572 : else
573 136 : joinOid = InvalidOid;
574 :
575 584 : if (commutatorName)
576 : {
577 : /* commutator has reversed arg types */
578 24 : commutatorOid = ValidateOperatorReference(commutatorName,
579 : oprForm->oprright,
580 : oprForm->oprleft);
581 :
582 : /*
583 : * We don't need to do anything extra for a self commutator as in
584 : * OperatorCreate, since the operator surely exists already.
585 : */
586 : }
587 : else
588 560 : commutatorOid = InvalidOid;
589 :
590 584 : if (negatorName)
591 : {
592 30 : negatorOid = ValidateOperatorReference(negatorName,
593 : oprForm->oprleft,
594 : oprForm->oprright);
595 :
596 : /* Must reject self-negation */
597 30 : if (negatorOid == oprForm->oid)
598 6 : ereport(ERROR,
599 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
600 : errmsg("operator cannot be its own negator")));
601 : }
602 : else
603 : {
604 554 : negatorOid = InvalidOid;
605 : }
606 :
607 : /*
608 : * Check that we're not changing any attributes that might be depended on
609 : * by plans, while allowing no-op updates.
610 : */
611 578 : if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) &&
612 12 : commutatorOid != oprForm->oprcom)
613 6 : ereport(ERROR,
614 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
615 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
616 : "commutator")));
617 :
618 572 : if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) &&
619 12 : negatorOid != oprForm->oprnegate)
620 6 : ereport(ERROR,
621 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
622 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
623 : "negator")));
624 :
625 566 : if (updateMerges && oprForm->oprcanmerge && !canMerge)
626 6 : ereport(ERROR,
627 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
628 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
629 : "merges")));
630 :
631 560 : if (updateHashes && oprForm->oprcanhash && !canHash)
632 6 : ereport(ERROR,
633 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
634 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
635 : "hashes")));
636 :
637 : /* Perform additional checks, like OperatorCreate does */
638 554 : OperatorValidateParams(oprForm->oprleft,
639 : oprForm->oprright,
640 : oprForm->oprresult,
641 : OidIsValid(commutatorOid),
642 : OidIsValid(negatorOid),
643 : OidIsValid(restrictionOid),
644 : OidIsValid(joinOid),
645 : canMerge,
646 : canHash);
647 :
648 : /* Update the tuple */
649 8864 : for (i = 0; i < Natts_pg_operator; ++i)
650 : {
651 8310 : values[i] = (Datum) 0;
652 8310 : replaces[i] = false;
653 8310 : nulls[i] = false;
654 : }
655 554 : if (updateRestriction)
656 : {
657 460 : replaces[Anum_pg_operator_oprrest - 1] = true;
658 460 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
659 : }
660 554 : if (updateJoin)
661 : {
662 460 : replaces[Anum_pg_operator_oprjoin - 1] = true;
663 460 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
664 : }
665 554 : if (OidIsValid(commutatorOid))
666 : {
667 18 : replaces[Anum_pg_operator_oprcom - 1] = true;
668 18 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
669 : }
670 554 : if (OidIsValid(negatorOid))
671 : {
672 18 : replaces[Anum_pg_operator_oprnegate - 1] = true;
673 18 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
674 : }
675 554 : if (updateMerges)
676 : {
677 18 : replaces[Anum_pg_operator_oprcanmerge - 1] = true;
678 18 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
679 : }
680 554 : if (updateHashes)
681 : {
682 28 : replaces[Anum_pg_operator_oprcanhash - 1] = true;
683 28 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
684 : }
685 :
686 554 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
687 : values, nulls, replaces);
688 :
689 554 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
690 :
691 554 : address = makeOperatorDependencies(tup, false, true);
692 :
693 554 : if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
694 36 : OperatorUpd(oprId, commutatorOid, negatorOid, false);
695 :
696 542 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
697 :
698 542 : table_close(catalog, NoLock);
699 :
700 542 : return address;
701 : }
|