1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_operator relation
6 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/catalog/pg_operator.c
14 * these routines moved here from commands/define.c and somewhat cleaned up.
16 *-------------------------------------------------------------------------
20 #include "access/htup_details.h"
21 #include "access/table.h"
22 #include "access/xact.h"
23 #include "catalog/catalog.h"
24 #include "catalog/dependency.h"
25 #include "catalog/indexing.h"
26 #include "catalog/namespace.h"
27 #include "catalog/objectaccess.h"
28 #include "catalog/pg_namespace.h"
29 #include "catalog/pg_operator.h"
30 #include "catalog/pg_proc.h"
31 #include "catalog/pg_type.h"
32 #include "miscadmin.h"
33 #include "parser/parse_oper.h"
34 #include "utils/acl.h"
35 #include "utils/builtins.h"
36 #include "utils/lsyscache.h"
37 #include "utils/rel.h"
38 #include "utils/syscache.h"
41 static Oid
OperatorGet(const char *operatorName
,
42 Oid operatorNamespace
,
47 static Oid
OperatorLookup(List
*operatorName
,
52 static Oid
OperatorShellMake(const char *operatorName
,
53 Oid operatorNamespace
,
57 static Oid
get_other_operator(List
*otherOp
,
58 Oid otherLeftTypeId
, Oid otherRightTypeId
,
59 const char *operatorName
, Oid operatorNamespace
,
60 Oid leftTypeId
, Oid rightTypeId
,
65 * Check whether a proposed operator name is legal
67 * This had better match the behavior of parser/scan.l!
69 * We need this because the parser is not smart enough to check that
70 * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
71 * are operator names rather than some other lexical entity.
74 validOperatorName(const char *name
)
76 size_t len
= strlen(name
);
78 /* Can't be empty or too long */
79 if (len
== 0 || len
>= NAMEDATALEN
)
82 /* Can't contain any invalid characters */
83 /* Test string here should match op_chars in scan.l */
84 if (strspn(name
, "~!@#^&|`?+-*/%<>=") != len
)
87 /* Can't contain slash-star or dash-dash (comment starts) */
88 if (strstr(name
, "/*") || strstr(name
, "--"))
92 * For SQL standard compatibility, '+' and '-' cannot be the last char of
93 * a multi-char operator unless the operator contains chars that are not
94 * in SQL operators. The idea is to lex '=-' as two operators, but not to
95 * forbid operator names like '?-' that could not be sequences of standard
99 (name
[len
- 1] == '+' ||
100 name
[len
- 1] == '-'))
104 for (ic
= len
- 2; ic
>= 0; ic
--)
106 if (strchr("~!@#^&|`?%", name
[ic
]))
110 return false; /* nope, not valid */
113 /* != isn't valid either, because parser will convert it to <> */
114 if (strcmp(name
, "!=") == 0)
124 * finds an operator given an exact specification (name, namespace,
125 * left and right type IDs).
127 * *defined is set true if defined (not a shell)
130 OperatorGet(const char *operatorName
,
131 Oid operatorNamespace
,
137 Oid operatorObjectId
;
139 tup
= SearchSysCache4(OPERNAMENSP
,
140 PointerGetDatum(operatorName
),
141 ObjectIdGetDatum(leftObjectId
),
142 ObjectIdGetDatum(rightObjectId
),
143 ObjectIdGetDatum(operatorNamespace
));
144 if (HeapTupleIsValid(tup
))
146 Form_pg_operator oprform
= (Form_pg_operator
) GETSTRUCT(tup
);
148 operatorObjectId
= oprform
->oid
;
149 *defined
= RegProcedureIsValid(oprform
->oprcode
);
150 ReleaseSysCache(tup
);
154 operatorObjectId
= InvalidOid
;
158 return operatorObjectId
;
164 * looks up an operator given a possibly-qualified name and
165 * left and right type IDs.
167 * *defined is set true if defined (not a shell)
170 OperatorLookup(List
*operatorName
,
175 Oid operatorObjectId
;
176 RegProcedure oprcode
;
178 operatorObjectId
= LookupOperName(NULL
, operatorName
,
179 leftObjectId
, rightObjectId
,
181 if (!OidIsValid(operatorObjectId
))
187 oprcode
= get_opcode(operatorObjectId
);
188 *defined
= RegProcedureIsValid(oprcode
);
190 return operatorObjectId
;
196 * Make a "shell" entry for a not-yet-existing operator.
199 OperatorShellMake(const char *operatorName
,
200 Oid operatorNamespace
,
204 Relation pg_operator_desc
;
205 Oid operatorObjectId
;
208 Datum values
[Natts_pg_operator
];
209 bool nulls
[Natts_pg_operator
];
214 * validate operator name
216 if (!validOperatorName(operatorName
))
218 (errcode(ERRCODE_INVALID_NAME
),
219 errmsg("\"%s\" is not a valid operator name",
225 pg_operator_desc
= table_open(OperatorRelationId
, RowExclusiveLock
);
226 tupDesc
= pg_operator_desc
->rd_att
;
229 * initialize our *nulls and *values arrays
231 for (i
= 0; i
< Natts_pg_operator
; ++i
)
234 values
[i
] = (Datum
) NULL
; /* redundant, but safe */
238 * initialize values[] with the operator name and input data types. Note
239 * that oprcode is set to InvalidOid, indicating it's a shell.
241 operatorObjectId
= GetNewOidWithIndex(pg_operator_desc
, OperatorOidIndexId
,
242 Anum_pg_operator_oid
);
243 values
[Anum_pg_operator_oid
- 1] = ObjectIdGetDatum(operatorObjectId
);
244 namestrcpy(&oname
, operatorName
);
245 values
[Anum_pg_operator_oprname
- 1] = NameGetDatum(&oname
);
246 values
[Anum_pg_operator_oprnamespace
- 1] = ObjectIdGetDatum(operatorNamespace
);
247 values
[Anum_pg_operator_oprowner
- 1] = ObjectIdGetDatum(GetUserId());
248 values
[Anum_pg_operator_oprkind
- 1] = CharGetDatum(leftTypeId
? (rightTypeId
? 'b' : 'r') : 'l');
249 values
[Anum_pg_operator_oprcanmerge
- 1] = BoolGetDatum(false);
250 values
[Anum_pg_operator_oprcanhash
- 1] = BoolGetDatum(false);
251 values
[Anum_pg_operator_oprleft
- 1] = ObjectIdGetDatum(leftTypeId
);
252 values
[Anum_pg_operator_oprright
- 1] = ObjectIdGetDatum(rightTypeId
);
253 values
[Anum_pg_operator_oprresult
- 1] = ObjectIdGetDatum(InvalidOid
);
254 values
[Anum_pg_operator_oprcom
- 1] = ObjectIdGetDatum(InvalidOid
);
255 values
[Anum_pg_operator_oprnegate
- 1] = ObjectIdGetDatum(InvalidOid
);
256 values
[Anum_pg_operator_oprcode
- 1] = ObjectIdGetDatum(InvalidOid
);
257 values
[Anum_pg_operator_oprrest
- 1] = ObjectIdGetDatum(InvalidOid
);
258 values
[Anum_pg_operator_oprjoin
- 1] = ObjectIdGetDatum(InvalidOid
);
261 * create a new operator tuple
263 tup
= heap_form_tuple(tupDesc
, values
, nulls
);
266 * insert our "shell" operator tuple
268 CatalogTupleInsert(pg_operator_desc
, tup
);
270 /* Add dependencies for the entry */
271 makeOperatorDependencies(tup
, false);
275 /* Post creation hook for new shell operator */
276 InvokeObjectPostCreateHook(OperatorRelationId
, operatorObjectId
, 0);
279 * Make sure the tuple is visible for subsequent lookups/updates.
281 CommandCounterIncrement();
284 * close the operator relation and return the oid.
286 table_close(pg_operator_desc
, RowExclusiveLock
);
288 return operatorObjectId
;
294 * "X" indicates an optional argument (i.e. one that can be NULL or 0)
295 * operatorName name for new operator
296 * operatorNamespace namespace for new operator
297 * leftTypeId X left type ID
298 * rightTypeId X right type ID
299 * procedureId procedure ID for operator
300 * commutatorName X commutator operator
301 * negatorName X negator operator
302 * restrictionId X restriction selectivity procedure ID
303 * joinId X join selectivity procedure ID
304 * canMerge merge join can be used with this operator
305 * canHash hash join can be used with this operator
307 * The caller should have validated properties and permissions for the
308 * objects passed as OID references. We must handle the commutator and
309 * negator operator references specially, however, since those need not
312 * This routine gets complicated because it allows the user to
313 * specify operators that do not exist. For example, if operator
314 * "op" is being defined, the negator operator "negop" and the
315 * commutator "commop" can also be defined without specifying
316 * any information other than their names. Since in order to
317 * add "op" to the PG_OPERATOR catalog, all the Oid's for these
318 * operators must be placed in the fields of "op", a forward
319 * declaration is done on the commutator and negator operators.
320 * This is called creating a shell, and its main effect is to
321 * create a tuple in the PG_OPERATOR catalog with minimal
322 * information about the operator (just its name and types).
323 * Forward declaration is used only for this purpose, it is
324 * not available to the user as it is for type definition.
327 OperatorCreate(const char *operatorName
,
328 Oid operatorNamespace
,
332 List
*commutatorName
,
339 Relation pg_operator_desc
;
342 bool nulls
[Natts_pg_operator
];
343 bool replaces
[Natts_pg_operator
];
344 Datum values
[Natts_pg_operator
];
345 Oid operatorObjectId
;
346 bool operatorAlreadyDefined
;
350 bool selfCommutator
= false;
353 ObjectAddress address
;
358 if (!validOperatorName(operatorName
))
360 (errcode(ERRCODE_INVALID_NAME
),
361 errmsg("\"%s\" is not a valid operator name",
364 if (!(OidIsValid(leftTypeId
) && OidIsValid(rightTypeId
)))
366 /* If it's not a binary op, these things mustn't be set: */
369 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
370 errmsg("only binary operators can have commutators")));
371 if (OidIsValid(joinId
))
373 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
374 errmsg("only binary operators can have join selectivity")));
377 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
378 errmsg("only binary operators can merge join")));
381 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
382 errmsg("only binary operators can hash")));
385 operResultType
= get_func_rettype(procedureId
);
387 if (operResultType
!= BOOLOID
)
389 /* If it's not a boolean op, these things mustn't be set: */
392 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
393 errmsg("only boolean operators can have negators")));
394 if (OidIsValid(restrictionId
))
396 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
397 errmsg("only boolean operators can have restriction selectivity")));
398 if (OidIsValid(joinId
))
400 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
401 errmsg("only boolean operators can have join selectivity")));
404 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
405 errmsg("only boolean operators can merge join")));
408 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
409 errmsg("only boolean operators can hash")));
412 operatorObjectId
= OperatorGet(operatorName
,
416 &operatorAlreadyDefined
);
418 if (operatorAlreadyDefined
)
420 (errcode(ERRCODE_DUPLICATE_FUNCTION
),
421 errmsg("operator %s already exists",
425 * At this point, if operatorObjectId is not InvalidOid then we are
426 * filling in a previously-created shell. Insist that the user own any
429 if (OidIsValid(operatorObjectId
) &&
430 !pg_oper_ownercheck(operatorObjectId
, GetUserId()))
431 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_OPERATOR
,
435 * Set up the other operators. If they do not currently exist, create
436 * shells in order to get ObjectId's.
441 /* commutator has reversed arg types */
442 commutatorId
= get_other_operator(commutatorName
,
443 rightTypeId
, leftTypeId
,
444 operatorName
, operatorNamespace
,
445 leftTypeId
, rightTypeId
,
448 /* Permission check: must own other operator */
449 if (OidIsValid(commutatorId
) &&
450 !pg_oper_ownercheck(commutatorId
, GetUserId()))
451 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_OPERATOR
,
452 NameListToString(commutatorName
));
455 * self-linkage to this operator; will fix below. Note that only
456 * self-linkage for commutation makes sense.
458 if (!OidIsValid(commutatorId
))
459 selfCommutator
= true;
462 commutatorId
= InvalidOid
;
466 /* negator has same arg types */
467 negatorId
= get_other_operator(negatorName
,
468 leftTypeId
, rightTypeId
,
469 operatorName
, operatorNamespace
,
470 leftTypeId
, rightTypeId
,
473 /* Permission check: must own other operator */
474 if (OidIsValid(negatorId
) &&
475 !pg_oper_ownercheck(negatorId
, GetUserId()))
476 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_OPERATOR
,
477 NameListToString(negatorName
));
480 negatorId
= InvalidOid
;
483 * set up values in the operator tuple
486 for (i
= 0; i
< Natts_pg_operator
; ++i
)
488 values
[i
] = (Datum
) NULL
;
493 namestrcpy(&oname
, operatorName
);
494 values
[Anum_pg_operator_oprname
- 1] = NameGetDatum(&oname
);
495 values
[Anum_pg_operator_oprnamespace
- 1] = ObjectIdGetDatum(operatorNamespace
);
496 values
[Anum_pg_operator_oprowner
- 1] = ObjectIdGetDatum(GetUserId());
497 values
[Anum_pg_operator_oprkind
- 1] = CharGetDatum(leftTypeId
? (rightTypeId
? 'b' : 'r') : 'l');
498 values
[Anum_pg_operator_oprcanmerge
- 1] = BoolGetDatum(canMerge
);
499 values
[Anum_pg_operator_oprcanhash
- 1] = BoolGetDatum(canHash
);
500 values
[Anum_pg_operator_oprleft
- 1] = ObjectIdGetDatum(leftTypeId
);
501 values
[Anum_pg_operator_oprright
- 1] = ObjectIdGetDatum(rightTypeId
);
502 values
[Anum_pg_operator_oprresult
- 1] = ObjectIdGetDatum(operResultType
);
503 values
[Anum_pg_operator_oprcom
- 1] = ObjectIdGetDatum(commutatorId
);
504 values
[Anum_pg_operator_oprnegate
- 1] = ObjectIdGetDatum(negatorId
);
505 values
[Anum_pg_operator_oprcode
- 1] = ObjectIdGetDatum(procedureId
);
506 values
[Anum_pg_operator_oprrest
- 1] = ObjectIdGetDatum(restrictionId
);
507 values
[Anum_pg_operator_oprjoin
- 1] = ObjectIdGetDatum(joinId
);
509 pg_operator_desc
= table_open(OperatorRelationId
, RowExclusiveLock
);
512 * If we are replacing an operator shell, update; else insert
514 if (operatorObjectId
)
518 tup
= SearchSysCacheCopy1(OPEROID
,
519 ObjectIdGetDatum(operatorObjectId
));
520 if (!HeapTupleIsValid(tup
))
521 elog(ERROR
, "cache lookup failed for operator %u",
524 replaces
[Anum_pg_operator_oid
- 1] = false;
525 tup
= heap_modify_tuple(tup
,
526 RelationGetDescr(pg_operator_desc
),
531 CatalogTupleUpdate(pg_operator_desc
, &tup
->t_self
, tup
);
537 operatorObjectId
= GetNewOidWithIndex(pg_operator_desc
,
539 Anum_pg_operator_oid
);
540 values
[Anum_pg_operator_oid
- 1] = ObjectIdGetDatum(operatorObjectId
);
542 tup
= heap_form_tuple(RelationGetDescr(pg_operator_desc
),
545 CatalogTupleInsert(pg_operator_desc
, tup
);
548 /* Add dependencies for the entry */
549 address
= makeOperatorDependencies(tup
, isUpdate
);
551 /* Post creation hook for new operator */
552 InvokeObjectPostCreateHook(OperatorRelationId
, operatorObjectId
, 0);
554 table_close(pg_operator_desc
, RowExclusiveLock
);
557 * If a commutator and/or negator link is provided, update the other
558 * operator(s) to point at this one, if they don't already have a link.
559 * This supports an alternative style of operator definition wherein the
560 * user first defines one operator without giving negator or commutator,
561 * then defines the other operator of the pair with the proper commutator
562 * or negator attribute. That style doesn't require creation of a shell,
563 * and it's the only style that worked right before Postgres version 6.5.
564 * This code also takes care of the situation where the new operator is
565 * its own commutator.
568 commutatorId
= operatorObjectId
;
570 if (OidIsValid(commutatorId
) || OidIsValid(negatorId
))
571 OperatorUpd(operatorObjectId
, commutatorId
, negatorId
, false);
577 * Try to lookup another operator (commutator, etc)
579 * If not found, check to see if it is exactly the operator we are trying
580 * to define; if so, return InvalidOid. (Note that this case is only
581 * sensible for a commutator, so we error out otherwise.) If it is not
582 * the same operator, create a shell operator.
585 get_other_operator(List
*otherOp
, Oid otherLeftTypeId
, Oid otherRightTypeId
,
586 const char *operatorName
, Oid operatorNamespace
,
587 Oid leftTypeId
, Oid rightTypeId
, bool isCommutator
)
595 other_oid
= OperatorLookup(otherOp
,
600 if (OidIsValid(other_oid
))
602 /* other op already in catalogs */
606 otherNamespace
= QualifiedNameGetCreationNamespace(otherOp
,
609 if (strcmp(otherName
, operatorName
) == 0 &&
610 otherNamespace
== operatorNamespace
&&
611 otherLeftTypeId
== leftTypeId
&&
612 otherRightTypeId
== rightTypeId
)
615 * self-linkage to this operator; caller will fix later. Note that
616 * only self-linkage for commutation makes sense.
620 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
621 errmsg("operator cannot be its own negator or sort operator")));
625 /* not in catalogs, different from operator, so make shell */
627 aclresult
= pg_namespace_aclcheck(otherNamespace
, GetUserId(),
629 if (aclresult
!= ACLCHECK_OK
)
630 aclcheck_error(aclresult
, OBJECT_SCHEMA
,
631 get_namespace_name(otherNamespace
));
633 other_oid
= OperatorShellMake(otherName
,
643 * For a given operator, look up its negator and commutator operators.
644 * When isDelete is false, update their negator and commutator fields to
645 * point back to the given operator; when isDelete is true, update those
646 * fields to no longer point back to the given operator.
648 * The !isDelete case solves a problem for users who need to insert two new
649 * operators that are the negator or commutator of each other, while the
650 * isDelete case is needed so as not to leave dangling OID links behind
651 * after dropping an operator.
654 OperatorUpd(Oid baseId
, Oid commId
, Oid negId
, bool isDelete
)
656 Relation pg_operator_desc
;
660 * If we're making an operator into its own commutator, then we need a
661 * command-counter increment here, since we've just inserted the tuple
662 * we're about to update. But when we're dropping an operator, we can
663 * skip this because we're at the beginning of the command.
666 CommandCounterIncrement();
668 /* Open the relation. */
669 pg_operator_desc
= table_open(OperatorRelationId
, RowExclusiveLock
);
671 /* Get a writable copy of the commutator's tuple. */
672 if (OidIsValid(commId
))
673 tup
= SearchSysCacheCopy1(OPEROID
, ObjectIdGetDatum(commId
));
677 /* Update the commutator's tuple if need be. */
678 if (HeapTupleIsValid(tup
))
680 Form_pg_operator t
= (Form_pg_operator
) GETSTRUCT(tup
);
681 bool update_commutator
= false;
684 * Out of due caution, we only change the commutator's oprcom field if
685 * it has the exact value we expected: InvalidOid when creating an
686 * operator, or baseId when dropping one.
688 if (isDelete
&& t
->oprcom
== baseId
)
690 t
->oprcom
= InvalidOid
;
691 update_commutator
= true;
693 else if (!isDelete
&& !OidIsValid(t
->oprcom
))
696 update_commutator
= true;
699 /* If any columns were found to need modification, update tuple. */
700 if (update_commutator
)
702 CatalogTupleUpdate(pg_operator_desc
, &tup
->t_self
, tup
);
705 * Do CCI to make the updated tuple visible. We must do this in
706 * case the commutator is also the negator. (Which would be a
707 * logic error on the operator definer's part, but that's not a
708 * good reason to fail here.) We would need a CCI anyway in the
709 * deletion case for a self-commutator with no negator.
711 CommandCounterIncrement();
716 * Similarly find and update the negator, if any.
718 if (OidIsValid(negId
))
719 tup
= SearchSysCacheCopy1(OPEROID
, ObjectIdGetDatum(negId
));
723 if (HeapTupleIsValid(tup
))
725 Form_pg_operator t
= (Form_pg_operator
) GETSTRUCT(tup
);
726 bool update_negator
= false;
729 * Out of due caution, we only change the negator's oprnegate field if
730 * it has the exact value we expected: InvalidOid when creating an
731 * operator, or baseId when dropping one.
733 if (isDelete
&& t
->oprnegate
== baseId
)
735 t
->oprnegate
= InvalidOid
;
736 update_negator
= true;
738 else if (!isDelete
&& !OidIsValid(t
->oprnegate
))
740 t
->oprnegate
= baseId
;
741 update_negator
= true;
744 /* If any columns were found to need modification, update tuple. */
747 CatalogTupleUpdate(pg_operator_desc
, &tup
->t_self
, tup
);
750 * In the deletion case, do CCI to make the updated tuple visible.
751 * We must do this in case the operator is its own negator. (Which
752 * would be a logic error on the operator definer's part, but
753 * that's not a good reason to fail here.)
756 CommandCounterIncrement();
760 /* Close relation and release catalog lock. */
761 table_close(pg_operator_desc
, RowExclusiveLock
);
765 * Create dependencies for an operator (either a freshly inserted
766 * complete operator, a new shell operator, a just-updated shell,
767 * or an operator that's being modified by ALTER OPERATOR).
769 * NB: the OidIsValid tests in this routine are necessary, in case
770 * the given operator is a shell.
773 makeOperatorDependencies(HeapTuple tuple
, bool isUpdate
)
775 Form_pg_operator oper
= (Form_pg_operator
) GETSTRUCT(tuple
);
776 ObjectAddress myself
,
779 ObjectAddressSet(myself
, OperatorRelationId
, oper
->oid
);
782 * If we are updating the operator, delete any existing entries, except
783 * for extension membership which should remain the same.
787 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
, true);
788 deleteSharedDependencyRecordsFor(myself
.classId
, myself
.objectId
, 0);
791 /* Dependency on namespace */
792 if (OidIsValid(oper
->oprnamespace
))
794 ObjectAddressSet(referenced
, NamespaceRelationId
, oper
->oprnamespace
);
795 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
798 /* Dependency on left type */
799 if (OidIsValid(oper
->oprleft
))
801 ObjectAddressSet(referenced
, TypeRelationId
, oper
->oprleft
);
802 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
805 /* Dependency on right type */
806 if (OidIsValid(oper
->oprright
))
808 ObjectAddressSet(referenced
, TypeRelationId
, oper
->oprright
);
809 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
812 /* Dependency on result type */
813 if (OidIsValid(oper
->oprresult
))
815 ObjectAddressSet(referenced
, TypeRelationId
, oper
->oprresult
);
816 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
820 * NOTE: we do not consider the operator to depend on the associated
821 * operators oprcom and oprnegate. We would not want to delete this
822 * operator if those go away, but only reset the link fields; which is not
823 * a function that the dependency code can presently handle. (Something
824 * could perhaps be done with objectSubId though.) For now, it's okay to
825 * let those links dangle if a referenced operator is removed.
828 /* Dependency on implementation function */
829 if (OidIsValid(oper
->oprcode
))
831 ObjectAddressSet(referenced
, ProcedureRelationId
, oper
->oprcode
);
832 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
835 /* Dependency on restriction selectivity function */
836 if (OidIsValid(oper
->oprrest
))
838 ObjectAddressSet(referenced
, ProcedureRelationId
, oper
->oprrest
);
839 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
842 /* Dependency on join selectivity function */
843 if (OidIsValid(oper
->oprjoin
))
845 ObjectAddressSet(referenced
, ProcedureRelationId
, oper
->oprjoin
);
846 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
849 /* Dependency on owner */
850 recordDependencyOnOwner(OperatorRelationId
, oper
->oid
,
853 /* Dependency on extension */
854 recordDependencyOnCurrentExtension(&myself
, true);