Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * operatorcmds.c
4 : *
5 : * Routines for operator manipulation commands
6 : *
7 : * Portions Copyright (c) 1996-2026, 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 "FooCreate" routines (in src/backend/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 1634 : DefineOperator(List *names, List *parameters)
68 : {
69 : char *oprName;
70 : Oid oprNamespace;
71 : AclResult aclresult;
72 1634 : bool canMerge = false; /* operator merges */
73 1634 : bool canHash = false; /* operator hashes */
74 1634 : List *functionName = NIL; /* function for operator */
75 1634 : TypeName *typeName1 = NULL; /* first type name */
76 1634 : TypeName *typeName2 = NULL; /* second type name */
77 1634 : Oid typeId1 = InvalidOid; /* types converted to OID */
78 1634 : Oid typeId2 = InvalidOid;
79 : Oid rettype;
80 1634 : List *commutatorName = NIL; /* optional commutator operator name */
81 1634 : List *negatorName = NIL; /* optional negator operator name */
82 1634 : List *restrictionName = NIL; /* optional restrict. sel. function */
83 1634 : 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 1634 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 :
94 : /* Check we have creation rights in target namespace */
95 1634 : aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
96 1634 : 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 10418 : foreach(pl, parameters)
104 : {
105 8802 : DefElem *defel = (DefElem *) lfirst(pl);
106 :
107 8802 : if (strcmp(defel->defname, "leftarg") == 0)
108 : {
109 1536 : typeName1 = defGetTypeName(defel);
110 1536 : if (typeName1->setof)
111 6 : ereport(ERROR,
112 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
113 : errmsg("SETOF type not allowed for operator argument")));
114 : }
115 7266 : else if (strcmp(defel->defname, "rightarg") == 0)
116 : {
117 1604 : typeName2 = defGetTypeName(defel);
118 1604 : 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 5662 : else if (strcmp(defel->defname, "function") == 0)
125 28 : functionName = defGetQualifiedName(defel);
126 5634 : else if (strcmp(defel->defname, "procedure") == 0)
127 1576 : functionName = defGetQualifiedName(defel);
128 4058 : else if (strcmp(defel->defname, "commutator") == 0)
129 1036 : commutatorName = defGetQualifiedName(defel);
130 3022 : else if (strcmp(defel->defname, "negator") == 0)
131 670 : negatorName = defGetQualifiedName(defel);
132 2352 : else if (strcmp(defel->defname, "restrict") == 0)
133 1028 : 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 1616 : 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 1604 : if (typeName1)
169 1530 : typeId1 = typenameTypeId(NULL, typeName1);
170 1604 : if (typeName2)
171 1592 : 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 1604 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
181 6 : ereport(ERROR,
182 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
183 : errmsg("operator argument types must be specified")));
184 1598 : 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 1592 : if (typeName1)
191 : {
192 1524 : aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
193 1524 : if (aclresult != ACLCHECK_OK)
194 12 : aclcheck_error_type(aclresult, typeId1);
195 : }
196 :
197 1580 : if (typeName2)
198 : {
199 1580 : aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
200 1580 : if (aclresult != ACLCHECK_OK)
201 6 : aclcheck_error_type(aclresult, typeId2);
202 : }
203 :
204 : /*
205 : * Look up the operator's underlying function.
206 : */
207 1574 : if (!OidIsValid(typeId1))
208 : {
209 68 : typeId[0] = typeId2;
210 68 : nargs = 1;
211 : }
212 1506 : else if (!OidIsValid(typeId2))
213 : {
214 0 : typeId[0] = typeId1;
215 0 : nargs = 1;
216 : }
217 : else
218 : {
219 1506 : typeId[0] = typeId1;
220 1506 : typeId[1] = typeId2;
221 1506 : nargs = 2;
222 : }
223 1574 : 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 1574 : aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
231 1574 : if (aclresult != ACLCHECK_OK)
232 6 : aclcheck_error(aclresult, OBJECT_FUNCTION,
233 6 : NameListToString(functionName));
234 :
235 1568 : rettype = get_func_rettype(functionOid);
236 1568 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
237 1568 : if (aclresult != ACLCHECK_OK)
238 6 : aclcheck_error_type(aclresult, rettype);
239 :
240 : /*
241 : * Look up restriction and join estimators if specified
242 : */
243 1562 : if (restrictionName)
244 1028 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
245 : else
246 534 : restrictionOid = InvalidOid;
247 1562 : if (joinName)
248 998 : joinOid = ValidateJoinEstimator(joinName);
249 : else
250 564 : joinOid = InvalidOid;
251 :
252 : /*
253 : * now have OperatorCreate do all the work..
254 : */
255 : return
256 1562 : 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 1482 : ValidateRestrictionEstimator(List *restrictionName)
276 : {
277 : Oid typeId[4];
278 : Oid restrictionOid;
279 :
280 1482 : typeId[0] = INTERNALOID; /* PlannerInfo */
281 1482 : typeId[1] = OIDOID; /* operator OID */
282 1482 : typeId[2] = INTERNALOID; /* args list */
283 1482 : typeId[3] = INT4OID; /* varRelid */
284 :
285 1482 : restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
286 :
287 : /* estimators must return float8 */
288 1476 : if (get_func_rettype(restrictionOid) != FLOAT8OID)
289 0 : ereport(ERROR,
290 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
291 : errmsg("restriction estimator function %s must return type %s",
292 : NameListToString(restrictionName), "float8")));
293 :
294 : /*
295 : * If the estimator is not a built-in function, require superuser
296 : * privilege to install it. This protects against using something that is
297 : * not a restriction estimator or has hard-wired assumptions about what
298 : * data types it is working with. (Built-in estimators are required to
299 : * defend themselves adequately against unexpected data type choices, but
300 : * it seems impractical to expect that of extensions' estimators.)
301 : *
302 : * If it is built-in, only require EXECUTE rights.
303 : */
304 1476 : if (restrictionOid >= FirstGenbkiObjectId)
305 : {
306 34 : if (!superuser())
307 0 : ereport(ERROR,
308 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
309 : errmsg("must be superuser to specify a non-built-in restriction estimator function")));
310 : }
311 : else
312 : {
313 : AclResult aclresult;
314 :
315 1442 : aclresult = object_aclcheck(ProcedureRelationId, restrictionOid,
316 : GetUserId(), ACL_EXECUTE);
317 1442 : if (aclresult != ACLCHECK_OK)
318 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
319 0 : NameListToString(restrictionName));
320 : }
321 :
322 1476 : return restrictionOid;
323 : }
324 :
325 : /*
326 : * Look up a join estimator function by name, and verify that it has the
327 : * correct signature and we have the permissions to attach it to an
328 : * operator.
329 : */
330 : static Oid
331 1452 : ValidateJoinEstimator(List *joinName)
332 : {
333 : Oid typeId[5];
334 : Oid joinOid;
335 : Oid joinOid2;
336 :
337 1452 : typeId[0] = INTERNALOID; /* PlannerInfo */
338 1452 : typeId[1] = OIDOID; /* operator OID */
339 1452 : typeId[2] = INTERNALOID; /* args list */
340 1452 : typeId[3] = INT2OID; /* jointype */
341 1452 : typeId[4] = INTERNALOID; /* SpecialJoinInfo */
342 :
343 : /*
344 : * As of Postgres 8.4, the preferred signature for join estimators has 5
345 : * arguments, but we still allow the old 4-argument form. Whine about
346 : * ambiguity if both forms exist.
347 : */
348 1452 : joinOid = LookupFuncName(joinName, 5, typeId, true);
349 1452 : joinOid2 = LookupFuncName(joinName, 4, typeId, true);
350 1452 : if (OidIsValid(joinOid))
351 : {
352 1446 : if (OidIsValid(joinOid2))
353 0 : ereport(ERROR,
354 : (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
355 : errmsg("join estimator function %s has multiple matches",
356 : NameListToString(joinName))));
357 : }
358 : else
359 : {
360 6 : joinOid = joinOid2;
361 : /* If not found, reference the 5-argument signature in error msg */
362 6 : if (!OidIsValid(joinOid))
363 6 : joinOid = LookupFuncName(joinName, 5, typeId, false);
364 : }
365 :
366 : /* estimators must return float8 */
367 1446 : if (get_func_rettype(joinOid) != FLOAT8OID)
368 0 : ereport(ERROR,
369 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
370 : errmsg("join estimator function %s must return type %s",
371 : NameListToString(joinName), "float8")));
372 :
373 : /* privilege checks are the same as in ValidateRestrictionEstimator */
374 1446 : if (joinOid >= FirstGenbkiObjectId)
375 : {
376 10 : if (!superuser())
377 0 : ereport(ERROR,
378 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
379 : errmsg("must be superuser to specify a non-built-in join estimator function")));
380 : }
381 : else
382 : {
383 : AclResult aclresult;
384 :
385 1436 : aclresult = object_aclcheck(ProcedureRelationId, joinOid,
386 : GetUserId(), ACL_EXECUTE);
387 1436 : if (aclresult != ACLCHECK_OK)
388 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
389 0 : NameListToString(joinName));
390 : }
391 :
392 1446 : return joinOid;
393 : }
394 :
395 : /*
396 : * Look up and return the OID of an operator,
397 : * given a possibly-qualified name and left and right type IDs.
398 : *
399 : * Verifies that the operator is defined (not a shell) and owned by
400 : * the current user, so that we have permission to associate it with
401 : * the operator being altered. Rejecting shell operators is a policy
402 : * choice to help catch mistakes, rather than something essential.
403 : */
404 : static Oid
405 54 : ValidateOperatorReference(List *name,
406 : Oid leftTypeId,
407 : Oid rightTypeId)
408 : {
409 : Oid oid;
410 : bool defined;
411 :
412 54 : oid = OperatorLookup(name,
413 : leftTypeId,
414 : rightTypeId,
415 : &defined);
416 :
417 : /* These message strings are chosen to match parse_oper.c */
418 54 : if (!OidIsValid(oid))
419 0 : ereport(ERROR,
420 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
421 : errmsg("operator does not exist: %s",
422 : op_signature_string(name,
423 : leftTypeId,
424 : rightTypeId))));
425 :
426 54 : if (!defined)
427 0 : ereport(ERROR,
428 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
429 : errmsg("operator is only a shell: %s",
430 : op_signature_string(name,
431 : leftTypeId,
432 : rightTypeId))));
433 :
434 54 : if (!object_ownercheck(OperatorRelationId, oid, GetUserId()))
435 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
436 0 : NameListToString(name));
437 :
438 54 : return oid;
439 : }
440 :
441 :
442 : /*
443 : * Guts of operator deletion.
444 : */
445 : void
446 788 : RemoveOperatorById(Oid operOid)
447 : {
448 : Relation relation;
449 : HeapTuple tup;
450 : Form_pg_operator op;
451 :
452 788 : relation = table_open(OperatorRelationId, RowExclusiveLock);
453 :
454 788 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
455 788 : if (!HeapTupleIsValid(tup)) /* should not happen */
456 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
457 788 : op = (Form_pg_operator) GETSTRUCT(tup);
458 :
459 : /*
460 : * Reset links from commutator and negator, if any. In case of a
461 : * self-commutator or self-negator, this means we have to re-fetch the
462 : * updated tuple. (We could optimize away updates on the tuple we're
463 : * about to drop, but it doesn't seem worth convoluting the logic for.)
464 : */
465 788 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
466 : {
467 378 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
468 378 : if (operOid == op->oprcom || operOid == op->oprnegate)
469 : {
470 148 : ReleaseSysCache(tup);
471 148 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
472 148 : if (!HeapTupleIsValid(tup)) /* should not happen */
473 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
474 : }
475 : }
476 :
477 788 : CatalogTupleDelete(relation, &tup->t_self);
478 :
479 788 : ReleaseSysCache(tup);
480 :
481 788 : table_close(relation, RowExclusiveLock);
482 788 : }
483 :
484 : /*
485 : * AlterOperator
486 : * routine implementing ALTER OPERATOR <operator> SET (option = ...).
487 : *
488 : * Currently, only RESTRICT and JOIN estimator functions can be changed.
489 : * COMMUTATOR, NEGATOR, MERGES, and HASHES attributes can be set if they
490 : * have not been set previously. (Changing or removing one of these
491 : * attributes could invalidate existing plans, which seems more trouble
492 : * than it's worth.)
493 : */
494 : ObjectAddress
495 608 : AlterOperator(AlterOperatorStmt *stmt)
496 : {
497 : ObjectAddress address;
498 : Oid oprId;
499 : Relation catalog;
500 : HeapTuple tup;
501 : Form_pg_operator oprForm;
502 : int i;
503 : ListCell *pl;
504 : Datum values[Natts_pg_operator];
505 : bool nulls[Natts_pg_operator];
506 : bool replaces[Natts_pg_operator];
507 608 : List *restrictionName = NIL; /* optional restrict. sel. function */
508 608 : bool updateRestriction = false;
509 : Oid restrictionOid;
510 608 : List *joinName = NIL; /* optional join sel. function */
511 608 : bool updateJoin = false;
512 : Oid joinOid;
513 608 : List *commutatorName = NIL; /* optional commutator operator name */
514 : Oid commutatorOid;
515 608 : List *negatorName = NIL; /* optional negator operator name */
516 : Oid negatorOid;
517 608 : bool canMerge = false;
518 608 : bool updateMerges = false;
519 608 : bool canHash = false;
520 608 : bool updateHashes = false;
521 :
522 : /* Look up the operator */
523 608 : oprId = LookupOperWithArgs(stmt->opername, false);
524 608 : catalog = table_open(OperatorRelationId, RowExclusiveLock);
525 608 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
526 608 : if (!HeapTupleIsValid(tup))
527 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
528 608 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
529 :
530 : /* Process options */
531 1658 : foreach(pl, stmt->options)
532 : {
533 1056 : DefElem *defel = (DefElem *) lfirst(pl);
534 : List *param;
535 :
536 1056 : if (defel->arg == NULL)
537 64 : param = NIL; /* NONE, removes the function */
538 : else
539 992 : param = defGetQualifiedName(defel);
540 :
541 1056 : if (strcmp(defel->defname, "restrict") == 0)
542 : {
543 472 : restrictionName = param;
544 472 : updateRestriction = true;
545 : }
546 584 : else if (strcmp(defel->defname, "join") == 0)
547 : {
548 466 : joinName = param;
549 466 : updateJoin = true;
550 : }
551 118 : else if (strcmp(defel->defname, "commutator") == 0)
552 : {
553 24 : commutatorName = defGetQualifiedName(defel);
554 : }
555 94 : else if (strcmp(defel->defname, "negator") == 0)
556 : {
557 30 : negatorName = defGetQualifiedName(defel);
558 : }
559 64 : else if (strcmp(defel->defname, "merges") == 0)
560 : {
561 24 : canMerge = defGetBoolean(defel);
562 24 : updateMerges = true;
563 : }
564 40 : else if (strcmp(defel->defname, "hashes") == 0)
565 : {
566 34 : canHash = defGetBoolean(defel);
567 34 : updateHashes = true;
568 : }
569 :
570 : /*
571 : * The rest of the options that CREATE accepts cannot be changed.
572 : * Check for them so that we can give a meaningful error message.
573 : */
574 6 : else if (strcmp(defel->defname, "leftarg") == 0 ||
575 6 : strcmp(defel->defname, "rightarg") == 0 ||
576 6 : strcmp(defel->defname, "function") == 0 ||
577 6 : strcmp(defel->defname, "procedure") == 0)
578 : {
579 0 : ereport(ERROR,
580 : (errcode(ERRCODE_SYNTAX_ERROR),
581 : errmsg("operator attribute \"%s\" cannot be changed",
582 : defel->defname)));
583 : }
584 : else
585 6 : ereport(ERROR,
586 : (errcode(ERRCODE_SYNTAX_ERROR),
587 : errmsg("operator attribute \"%s\" not recognized",
588 : defel->defname)));
589 : }
590 :
591 : /* Check permissions. Must be owner. */
592 602 : if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
593 6 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
594 6 : NameStr(oprForm->oprname));
595 :
596 : /*
597 : * Look up OIDs for any parameters specified
598 : */
599 596 : if (restrictionName)
600 454 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
601 : else
602 142 : restrictionOid = InvalidOid;
603 590 : if (joinName)
604 454 : joinOid = ValidateJoinEstimator(joinName);
605 : else
606 136 : joinOid = InvalidOid;
607 :
608 584 : if (commutatorName)
609 : {
610 : /* commutator has reversed arg types */
611 24 : commutatorOid = ValidateOperatorReference(commutatorName,
612 : oprForm->oprright,
613 : oprForm->oprleft);
614 :
615 : /*
616 : * We don't need to do anything extra for a self commutator as in
617 : * OperatorCreate, since the operator surely exists already.
618 : */
619 : }
620 : else
621 560 : commutatorOid = InvalidOid;
622 :
623 584 : if (negatorName)
624 : {
625 30 : negatorOid = ValidateOperatorReference(negatorName,
626 : oprForm->oprleft,
627 : oprForm->oprright);
628 :
629 : /* Must reject self-negation */
630 30 : if (negatorOid == oprForm->oid)
631 6 : ereport(ERROR,
632 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
633 : errmsg("operator cannot be its own negator")));
634 : }
635 : else
636 : {
637 554 : negatorOid = InvalidOid;
638 : }
639 :
640 : /*
641 : * Check that we're not changing any attributes that might be depended on
642 : * by plans, while allowing no-op updates.
643 : */
644 578 : if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) &&
645 12 : commutatorOid != oprForm->oprcom)
646 6 : ereport(ERROR,
647 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
648 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
649 : "commutator")));
650 :
651 572 : if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) &&
652 12 : negatorOid != oprForm->oprnegate)
653 6 : ereport(ERROR,
654 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
655 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
656 : "negator")));
657 :
658 566 : if (updateMerges && oprForm->oprcanmerge && !canMerge)
659 6 : ereport(ERROR,
660 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
661 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
662 : "merges")));
663 :
664 560 : if (updateHashes && oprForm->oprcanhash && !canHash)
665 6 : ereport(ERROR,
666 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
667 : errmsg("operator attribute \"%s\" cannot be changed if it has already been set",
668 : "hashes")));
669 :
670 : /* Perform additional checks, like OperatorCreate does */
671 554 : OperatorValidateParams(oprForm->oprleft,
672 : oprForm->oprright,
673 : oprForm->oprresult,
674 : OidIsValid(commutatorOid),
675 : OidIsValid(negatorOid),
676 : OidIsValid(restrictionOid),
677 : OidIsValid(joinOid),
678 : canMerge,
679 : canHash);
680 :
681 : /* Update the tuple */
682 8864 : for (i = 0; i < Natts_pg_operator; ++i)
683 : {
684 8310 : values[i] = (Datum) 0;
685 8310 : replaces[i] = false;
686 8310 : nulls[i] = false;
687 : }
688 554 : if (updateRestriction)
689 : {
690 460 : replaces[Anum_pg_operator_oprrest - 1] = true;
691 460 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
692 : }
693 554 : if (updateJoin)
694 : {
695 460 : replaces[Anum_pg_operator_oprjoin - 1] = true;
696 460 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
697 : }
698 554 : if (OidIsValid(commutatorOid))
699 : {
700 18 : replaces[Anum_pg_operator_oprcom - 1] = true;
701 18 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
702 : }
703 554 : if (OidIsValid(negatorOid))
704 : {
705 18 : replaces[Anum_pg_operator_oprnegate - 1] = true;
706 18 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
707 : }
708 554 : if (updateMerges)
709 : {
710 18 : replaces[Anum_pg_operator_oprcanmerge - 1] = true;
711 18 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
712 : }
713 554 : if (updateHashes)
714 : {
715 28 : replaces[Anum_pg_operator_oprcanhash - 1] = true;
716 28 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
717 : }
718 :
719 554 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
720 : values, nulls, replaces);
721 :
722 554 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
723 :
724 554 : address = makeOperatorDependencies(tup, false, true);
725 :
726 554 : if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
727 36 : OperatorUpd(oprId, commutatorOid, negatorOid, false);
728 :
729 542 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
730 :
731 542 : table_close(catalog, NoLock);
732 :
733 542 : return address;
734 : }
|