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 826 : DefineOperator(List *names, List *parameters)
68 : {
69 : char *oprName;
70 : Oid oprNamespace;
71 : AclResult aclresult;
72 826 : bool canMerge = false; /* operator merges */
73 826 : bool canHash = false; /* operator hashes */
74 826 : List *functionName = NIL; /* function for operator */
75 826 : TypeName *typeName1 = NULL; /* first type name */
76 826 : TypeName *typeName2 = NULL; /* second type name */
77 826 : Oid typeId1 = InvalidOid; /* types converted to OID */
78 826 : Oid typeId2 = InvalidOid;
79 : Oid rettype;
80 826 : List *commutatorName = NIL; /* optional commutator operator name */
81 826 : List *negatorName = NIL; /* optional negator operator name */
82 826 : List *restrictionName = NIL; /* optional restrict. sel. function */
83 826 : 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 826 : oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
93 :
94 : /* Check we have creation rights in target namespace */
95 826 : aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
96 826 : if (aclresult != ACLCHECK_OK)
97 3 : aclcheck_error(aclresult, OBJECT_SCHEMA,
98 3 : get_namespace_name(oprNamespace));
99 :
100 : /*
101 : * loop over the definition list and extract the information we need.
102 : */
103 5271 : foreach(pl, parameters)
104 : {
105 4454 : DefElem *defel = (DefElem *) lfirst(pl);
106 :
107 4454 : if (strcmp(defel->defname, "leftarg") == 0)
108 : {
109 777 : typeName1 = defGetTypeName(defel);
110 777 : if (typeName1->setof)
111 3 : ereport(ERROR,
112 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
113 : errmsg("SETOF type not allowed for operator argument")));
114 : }
115 3677 : else if (strcmp(defel->defname, "rightarg") == 0)
116 : {
117 811 : typeName2 = defGetTypeName(defel);
118 811 : if (typeName2->setof)
119 3 : 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 2866 : else if (strcmp(defel->defname, "function") == 0)
125 20 : functionName = defGetQualifiedName(defel);
126 2846 : else if (strcmp(defel->defname, "procedure") == 0)
127 791 : functionName = defGetQualifiedName(defel);
128 2055 : else if (strcmp(defel->defname, "commutator") == 0)
129 524 : commutatorName = defGetQualifiedName(defel);
130 1531 : else if (strcmp(defel->defname, "negator") == 0)
131 341 : negatorName = defGetQualifiedName(defel);
132 1190 : else if (strcmp(defel->defname, "restrict") == 0)
133 520 : restrictionName = defGetQualifiedName(defel);
134 670 : else if (strcmp(defel->defname, "join") == 0)
135 505 : joinName = defGetQualifiedName(defel);
136 165 : else if (strcmp(defel->defname, "hashes") == 0)
137 47 : canHash = defGetBoolean(defel);
138 118 : else if (strcmp(defel->defname, "merges") == 0)
139 72 : canMerge = defGetBoolean(defel);
140 : /* These obsolete options are taken as meaning canMerge */
141 46 : else if (strcmp(defel->defname, "sort1") == 0)
142 5 : canMerge = true;
143 41 : else if (strcmp(defel->defname, "sort2") == 0)
144 5 : canMerge = true;
145 36 : else if (strcmp(defel->defname, "ltcmp") == 0)
146 3 : canMerge = true;
147 33 : else if (strcmp(defel->defname, "gtcmp") == 0)
148 3 : canMerge = true;
149 : else
150 : {
151 : /* WARNING, not ERROR, for historical backwards-compatibility */
152 30 : 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 817 : if (functionName == NIL)
163 6 : ereport(ERROR,
164 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
165 : errmsg("operator function must be specified")));
166 :
167 : /* Transform type names to type OIDs */
168 811 : if (typeName1)
169 774 : typeId1 = typenameTypeId(NULL, typeName1);
170 811 : if (typeName2)
171 805 : 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 811 : if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
181 3 : ereport(ERROR,
182 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
183 : errmsg("operator argument types must be specified")));
184 808 : if (!OidIsValid(typeId2))
185 3 : 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 805 : if (typeName1)
191 : {
192 771 : aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
193 771 : if (aclresult != ACLCHECK_OK)
194 6 : aclcheck_error_type(aclresult, typeId1);
195 : }
196 :
197 799 : if (typeName2)
198 : {
199 799 : aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
200 799 : if (aclresult != ACLCHECK_OK)
201 3 : aclcheck_error_type(aclresult, typeId2);
202 : }
203 :
204 : /*
205 : * Look up the operator's underlying function.
206 : */
207 796 : if (!OidIsValid(typeId1))
208 : {
209 34 : typeId[0] = typeId2;
210 34 : nargs = 1;
211 : }
212 762 : else if (!OidIsValid(typeId2))
213 : {
214 0 : typeId[0] = typeId1;
215 0 : nargs = 1;
216 : }
217 : else
218 : {
219 762 : typeId[0] = typeId1;
220 762 : typeId[1] = typeId2;
221 762 : nargs = 2;
222 : }
223 796 : 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 796 : aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
231 796 : if (aclresult != ACLCHECK_OK)
232 3 : aclcheck_error(aclresult, OBJECT_FUNCTION,
233 3 : NameListToString(functionName));
234 :
235 793 : rettype = get_func_rettype(functionOid);
236 793 : aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
237 793 : if (aclresult != ACLCHECK_OK)
238 3 : aclcheck_error_type(aclresult, rettype);
239 :
240 : /*
241 : * Look up restriction and join estimators if specified
242 : */
243 790 : if (restrictionName)
244 520 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
245 : else
246 270 : restrictionOid = InvalidOid;
247 790 : if (joinName)
248 505 : joinOid = ValidateJoinEstimator(joinName);
249 : else
250 285 : joinOid = InvalidOid;
251 :
252 : /*
253 : * now have OperatorCreate do all the work..
254 : */
255 : return
256 790 : 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 747 : ValidateRestrictionEstimator(List *restrictionName)
276 : {
277 : Oid typeId[4];
278 : Oid restrictionOid;
279 :
280 747 : typeId[0] = INTERNALOID; /* PlannerInfo */
281 747 : typeId[1] = OIDOID; /* operator OID */
282 747 : typeId[2] = INTERNALOID; /* args list */
283 747 : typeId[3] = INT4OID; /* varRelid */
284 :
285 747 : restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
286 :
287 : /* estimators must return float8 */
288 744 : 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 744 : if (restrictionOid >= FirstGenbkiObjectId)
305 : {
306 17 : 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 727 : aclresult = object_aclcheck(ProcedureRelationId, restrictionOid,
316 : GetUserId(), ACL_EXECUTE);
317 727 : if (aclresult != ACLCHECK_OK)
318 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
319 0 : NameListToString(restrictionName));
320 : }
321 :
322 744 : 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 732 : ValidateJoinEstimator(List *joinName)
332 : {
333 : Oid typeId[5];
334 : Oid joinOid;
335 : Oid joinOid2;
336 :
337 732 : typeId[0] = INTERNALOID; /* PlannerInfo */
338 732 : typeId[1] = OIDOID; /* operator OID */
339 732 : typeId[2] = INTERNALOID; /* args list */
340 732 : typeId[3] = INT2OID; /* jointype */
341 732 : 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 732 : joinOid = LookupFuncName(joinName, 5, typeId, true);
349 732 : joinOid2 = LookupFuncName(joinName, 4, typeId, true);
350 732 : if (OidIsValid(joinOid))
351 : {
352 729 : 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 3 : joinOid = joinOid2;
361 : /* If not found, reference the 5-argument signature in error msg */
362 3 : if (!OidIsValid(joinOid))
363 3 : joinOid = LookupFuncName(joinName, 5, typeId, false);
364 : }
365 :
366 : /* estimators must return float8 */
367 729 : 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 729 : if (joinOid >= FirstGenbkiObjectId)
375 : {
376 5 : 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 724 : aclresult = object_aclcheck(ProcedureRelationId, joinOid,
386 : GetUserId(), ACL_EXECUTE);
387 724 : if (aclresult != ACLCHECK_OK)
388 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
389 0 : NameListToString(joinName));
390 : }
391 :
392 729 : 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 27 : ValidateOperatorReference(List *name,
406 : Oid leftTypeId,
407 : Oid rightTypeId)
408 : {
409 : Oid oid;
410 : bool defined;
411 :
412 27 : oid = OperatorLookup(name,
413 : leftTypeId,
414 : rightTypeId,
415 : &defined);
416 :
417 : /* These message strings are chosen to match parse_oper.c */
418 27 : 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 27 : 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 27 : if (!object_ownercheck(OperatorRelationId, oid, GetUserId()))
435 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
436 0 : NameListToString(name));
437 :
438 27 : return oid;
439 : }
440 :
441 :
442 : /*
443 : * Guts of operator deletion.
444 : */
445 : void
446 400 : RemoveOperatorById(Oid operOid)
447 : {
448 : Relation relation;
449 : HeapTuple tup;
450 : Form_pg_operator op;
451 :
452 400 : relation = table_open(OperatorRelationId, RowExclusiveLock);
453 :
454 400 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
455 400 : if (!HeapTupleIsValid(tup)) /* should not happen */
456 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
457 400 : 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 400 : if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
466 : {
467 193 : OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
468 193 : if (operOid == op->oprcom || operOid == op->oprnegate)
469 : {
470 76 : ReleaseSysCache(tup);
471 76 : tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
472 76 : if (!HeapTupleIsValid(tup)) /* should not happen */
473 0 : elog(ERROR, "cache lookup failed for operator %u", operOid);
474 : }
475 : }
476 :
477 400 : CatalogTupleDelete(relation, &tup->t_self);
478 :
479 400 : ReleaseSysCache(tup);
480 :
481 400 : table_close(relation, RowExclusiveLock);
482 400 : }
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 304 : 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 304 : List *restrictionName = NIL; /* optional restrict. sel. function */
508 304 : bool updateRestriction = false;
509 : Oid restrictionOid;
510 304 : List *joinName = NIL; /* optional join sel. function */
511 304 : bool updateJoin = false;
512 : Oid joinOid;
513 304 : List *commutatorName = NIL; /* optional commutator operator name */
514 : Oid commutatorOid;
515 304 : List *negatorName = NIL; /* optional negator operator name */
516 : Oid negatorOid;
517 304 : bool canMerge = false;
518 304 : bool updateMerges = false;
519 304 : bool canHash = false;
520 304 : bool updateHashes = false;
521 :
522 : /* Look up the operator */
523 304 : oprId = LookupOperWithArgs(stmt->opername, false);
524 304 : catalog = table_open(OperatorRelationId, RowExclusiveLock);
525 304 : tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
526 304 : if (!HeapTupleIsValid(tup))
527 0 : elog(ERROR, "cache lookup failed for operator %u", oprId);
528 304 : oprForm = (Form_pg_operator) GETSTRUCT(tup);
529 :
530 : /* Process options */
531 829 : foreach(pl, stmt->options)
532 : {
533 528 : DefElem *defel = (DefElem *) lfirst(pl);
534 : List *param;
535 :
536 528 : if (defel->arg == NULL)
537 32 : param = NIL; /* NONE, removes the function */
538 : else
539 496 : param = defGetQualifiedName(defel);
540 :
541 528 : if (strcmp(defel->defname, "restrict") == 0)
542 : {
543 236 : restrictionName = param;
544 236 : updateRestriction = true;
545 : }
546 292 : else if (strcmp(defel->defname, "join") == 0)
547 : {
548 233 : joinName = param;
549 233 : updateJoin = true;
550 : }
551 59 : else if (strcmp(defel->defname, "commutator") == 0)
552 : {
553 12 : commutatorName = defGetQualifiedName(defel);
554 : }
555 47 : else if (strcmp(defel->defname, "negator") == 0)
556 : {
557 15 : negatorName = defGetQualifiedName(defel);
558 : }
559 32 : else if (strcmp(defel->defname, "merges") == 0)
560 : {
561 12 : canMerge = defGetBoolean(defel);
562 12 : updateMerges = true;
563 : }
564 20 : else if (strcmp(defel->defname, "hashes") == 0)
565 : {
566 17 : canHash = defGetBoolean(defel);
567 17 : 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 3 : else if (strcmp(defel->defname, "leftarg") == 0 ||
575 3 : strcmp(defel->defname, "rightarg") == 0 ||
576 3 : strcmp(defel->defname, "function") == 0 ||
577 3 : 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 3 : 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 301 : if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
593 3 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
594 3 : NameStr(oprForm->oprname));
595 :
596 : /*
597 : * Look up OIDs for any parameters specified
598 : */
599 298 : if (restrictionName)
600 227 : restrictionOid = ValidateRestrictionEstimator(restrictionName);
601 : else
602 71 : restrictionOid = InvalidOid;
603 295 : if (joinName)
604 227 : joinOid = ValidateJoinEstimator(joinName);
605 : else
606 68 : joinOid = InvalidOid;
607 :
608 292 : if (commutatorName)
609 : {
610 : /* commutator has reversed arg types */
611 12 : 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 280 : commutatorOid = InvalidOid;
622 :
623 292 : if (negatorName)
624 : {
625 15 : negatorOid = ValidateOperatorReference(negatorName,
626 : oprForm->oprleft,
627 : oprForm->oprright);
628 :
629 : /* Must reject self-negation */
630 15 : if (negatorOid == oprForm->oid)
631 3 : ereport(ERROR,
632 : (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
633 : errmsg("operator cannot be its own negator")));
634 : }
635 : else
636 : {
637 277 : 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 289 : if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) &&
645 6 : commutatorOid != oprForm->oprcom)
646 3 : 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 286 : if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) &&
652 6 : negatorOid != oprForm->oprnegate)
653 3 : 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 283 : if (updateMerges && oprForm->oprcanmerge && !canMerge)
659 3 : 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 280 : if (updateHashes && oprForm->oprcanhash && !canHash)
665 3 : 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 277 : 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 4432 : for (i = 0; i < Natts_pg_operator; ++i)
683 : {
684 4155 : values[i] = (Datum) 0;
685 4155 : replaces[i] = false;
686 4155 : nulls[i] = false;
687 : }
688 277 : if (updateRestriction)
689 : {
690 230 : replaces[Anum_pg_operator_oprrest - 1] = true;
691 230 : values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
692 : }
693 277 : if (updateJoin)
694 : {
695 230 : replaces[Anum_pg_operator_oprjoin - 1] = true;
696 230 : values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
697 : }
698 277 : if (OidIsValid(commutatorOid))
699 : {
700 9 : replaces[Anum_pg_operator_oprcom - 1] = true;
701 9 : values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid);
702 : }
703 277 : if (OidIsValid(negatorOid))
704 : {
705 9 : replaces[Anum_pg_operator_oprnegate - 1] = true;
706 9 : values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid);
707 : }
708 277 : if (updateMerges)
709 : {
710 9 : replaces[Anum_pg_operator_oprcanmerge - 1] = true;
711 9 : values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
712 : }
713 277 : if (updateHashes)
714 : {
715 14 : replaces[Anum_pg_operator_oprcanhash - 1] = true;
716 14 : values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
717 : }
718 :
719 277 : tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
720 : values, nulls, replaces);
721 :
722 277 : CatalogTupleUpdate(catalog, &tup->t_self, tup);
723 :
724 277 : address = makeOperatorDependencies(tup, false, true);
725 :
726 277 : if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
727 18 : OperatorUpd(oprId, commutatorOid, negatorOid, false);
728 :
729 271 : InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
730 :
731 271 : table_close(catalog, NoLock);
732 :
733 271 : return address;
734 : }
|