1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_operator relation
6 * Portions Copyright (c) 1996-2013, 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/heapam.h"
21 #include "access/htup_details.h"
22 #include "access/xact.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/namespace.h"
26 #include "catalog/objectaccess.h"
27 #include "catalog/pg_namespace.h"
28 #include "catalog/pg_operator.h"
29 #include "catalog/pg_proc.h"
30 #include "catalog/pg_type.h"
31 #include "miscadmin.h"
32 #include "parser/parse_oper.h"
33 #include "utils/acl.h"
34 #include "utils/builtins.h"
35 #include "utils/lsyscache.h"
36 #include "utils/rel.h"
37 #include "utils/syscache.h"
40 static Oid OperatorGet(const char *operatorName,
41 Oid operatorNamespace,
46 static Oid OperatorLookup(List *operatorName,
51 static Oid OperatorShellMake(const char *operatorName,
52 Oid operatorNamespace,
56 static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
58 static Oid get_other_operator(List *otherOp,
59 Oid otherLeftTypeId, Oid otherRightTypeId,
60 const char *operatorName, Oid operatorNamespace,
61 Oid leftTypeId, Oid rightTypeId,
64 static void makeOperatorDependencies(HeapTuple tuple);
68 * Check whether a proposed operator name is legal
70 * This had better match the behavior of parser/scan.l!
72 * We need this because the parser is not smart enough to check that
73 * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
74 * are operator names rather than some other lexical entity.
77 validOperatorName(const char *name)
79 size_t len = strlen(name);
81 /* Can't be empty or too long */
82 if (len == 0 || len >= NAMEDATALEN)
85 /* Can't contain any invalid characters */
86 /* Test string here should match op_chars in scan.l */
87 if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
90 /* Can't contain slash-star or dash-dash (comment starts) */
91 if (strstr(name, "/*") || strstr(name, "--"))
95 * For SQL92 compatibility, '+' and '-' cannot be the last char of a
96 * multi-char operator unless the operator contains chars that are not in
97 * SQL92 operators. The idea is to lex '=-' as two operators, but not to
98 * forbid operator names like '?-' that could not be sequences of SQL92
102 (name[len - 1] == '+' ||
103 name[len - 1] == '-'))
107 for (ic = len - 2; ic >= 0; ic--)
109 if (strchr("~!@#^&|`?%", name[ic]))
113 return false; /* nope, not valid */
116 /* != isn't valid either, because parser will convert it to <> */
117 if (strcmp(name, "!=") == 0)
127 * finds an operator given an exact specification (name, namespace,
128 * left and right type IDs).
130 * *defined is set TRUE if defined (not a shell)
133 OperatorGet(const char *operatorName,
134 Oid operatorNamespace,
140 Oid operatorObjectId;
142 tup = SearchSysCache4(OPERNAMENSP,
143 PointerGetDatum(operatorName),
144 ObjectIdGetDatum(leftObjectId),
145 ObjectIdGetDatum(rightObjectId),
146 ObjectIdGetDatum(operatorNamespace));
147 if (HeapTupleIsValid(tup))
149 RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
151 operatorObjectId = HeapTupleGetOid(tup);
152 *defined = RegProcedureIsValid(oprcode);
153 ReleaseSysCache(tup);
157 operatorObjectId = InvalidOid;
161 return operatorObjectId;
167 * looks up an operator given a possibly-qualified name and
168 * left and right type IDs.
170 * *defined is set TRUE if defined (not a shell)
173 OperatorLookup(List *operatorName,
178 Oid operatorObjectId;
179 RegProcedure oprcode;
181 operatorObjectId = LookupOperName(NULL, operatorName,
182 leftObjectId, rightObjectId,
184 if (!OidIsValid(operatorObjectId))
190 oprcode = get_opcode(operatorObjectId);
191 *defined = RegProcedureIsValid(oprcode);
193 return operatorObjectId;
199 * Make a "shell" entry for a not-yet-existing operator.
202 OperatorShellMake(const char *operatorName,
203 Oid operatorNamespace,
207 Relation pg_operator_desc;
208 Oid operatorObjectId;
211 Datum values[Natts_pg_operator];
212 bool nulls[Natts_pg_operator];
217 * validate operator name
219 if (!validOperatorName(operatorName))
221 (errcode(ERRCODE_INVALID_NAME),
222 errmsg("\"%s\" is not a valid operator name",
226 * initialize our *nulls and *values arrays
228 for (i = 0; i < Natts_pg_operator; ++i)
231 values[i] = (Datum) NULL; /* redundant, but safe */
235 * initialize values[] with the operator name and input data types. Note
236 * that oprcode is set to InvalidOid, indicating it's a shell.
238 namestrcpy(&oname, operatorName);
239 values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
240 values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
241 values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
242 values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l');
243 values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
244 values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
245 values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
246 values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
247 values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
248 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
249 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
250 values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
251 values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
252 values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
257 pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
258 tupDesc = pg_operator_desc->rd_att;
261 * create a new operator tuple
263 tup = heap_form_tuple(tupDesc, values, nulls);
266 * insert our "shell" operator tuple
268 operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
270 CatalogUpdateIndexes(pg_operator_desc, tup);
272 /* Add dependencies for the entry */
273 makeOperatorDependencies(tup);
277 /* Post creation hook for new shell operator */
278 InvokeObjectAccessHook(OAT_POST_CREATE,
279 OperatorRelationId, operatorObjectId, 0, NULL);
282 * Make sure the tuple is visible for subsequent lookups/updates.
284 CommandCounterIncrement();
287 * close the operator relation and return the oid.
289 heap_close(pg_operator_desc, RowExclusiveLock);
291 return operatorObjectId;
297 * "X" indicates an optional argument (i.e. one that can be NULL or 0)
298 * operatorName name for new operator
299 * operatorNamespace namespace for new operator
300 * leftTypeId X left type ID
301 * rightTypeId X right type ID
302 * procedureId procedure ID for operator
303 * commutatorName X commutator operator
304 * negatorName X negator operator
305 * restrictionId X restriction selectivity procedure ID
306 * joinId X join selectivity procedure ID
307 * canMerge merge join can be used with this operator
308 * canHash hash join can be used with this operator
310 * The caller should have validated properties and permissions for the
311 * objects passed as OID references. We must handle the commutator and
312 * negator operator references specially, however, since those need not
315 * This routine gets complicated because it allows the user to
316 * specify operators that do not exist. For example, if operator
317 * "op" is being defined, the negator operator "negop" and the
318 * commutator "commop" can also be defined without specifying
319 * any information other than their names. Since in order to
320 * add "op" to the PG_OPERATOR catalog, all the Oid's for these
321 * operators must be placed in the fields of "op", a forward
322 * declaration is done on the commutator and negator operators.
323 * This is called creating a shell, and its main effect is to
324 * create a tuple in the PG_OPERATOR catalog with minimal
325 * information about the operator (just its name and types).
326 * Forward declaration is used only for this purpose, it is
327 * not available to the user as it is for type definition.
330 OperatorCreate(const char *operatorName,
331 Oid operatorNamespace,
335 List *commutatorName,
342 Relation pg_operator_desc;
344 bool nulls[Natts_pg_operator];
345 bool replaces[Natts_pg_operator];
346 Datum values[Natts_pg_operator];
347 Oid operatorObjectId;
348 bool operatorAlreadyDefined;
352 bool selfCommutator = false;
360 if (!validOperatorName(operatorName))
362 (errcode(ERRCODE_INVALID_NAME),
363 errmsg("\"%s\" is not a valid operator name",
366 if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
368 /* If it's not a binary op, these things mustn't be set: */
371 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
372 errmsg("only binary operators can have commutators")));
373 if (OidIsValid(joinId))
375 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
376 errmsg("only binary operators can have join selectivity")));
379 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
380 errmsg("only binary operators can merge join")));
383 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
384 errmsg("only binary operators can hash")));
387 operResultType = get_func_rettype(procedureId);
389 if (operResultType != BOOLOID)
391 /* If it's not a boolean op, these things mustn't be set: */
394 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
395 errmsg("only boolean operators can have negators")));
396 if (OidIsValid(restrictionId))
398 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
399 errmsg("only boolean operators can have restriction selectivity")));
400 if (OidIsValid(joinId))
402 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
403 errmsg("only boolean operators can have join selectivity")));
406 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
407 errmsg("only boolean operators can merge join")));
410 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
411 errmsg("only boolean operators can hash")));
414 operatorObjectId = OperatorGet(operatorName,
418 &operatorAlreadyDefined);
420 if (operatorAlreadyDefined)
422 (errcode(ERRCODE_DUPLICATE_FUNCTION),
423 errmsg("operator %s already exists",
427 * At this point, if operatorObjectId is not InvalidOid then we are
428 * filling in a previously-created shell. Insist that the user own any
431 if (OidIsValid(operatorObjectId) &&
432 !pg_oper_ownercheck(operatorObjectId, GetUserId()))
433 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
437 * Set up the other operators. If they do not currently exist, create
438 * shells in order to get ObjectId's.
443 /* commutator has reversed arg types */
444 commutatorId = get_other_operator(commutatorName,
445 rightTypeId, leftTypeId,
446 operatorName, operatorNamespace,
447 leftTypeId, rightTypeId,
450 /* Permission check: must own other operator */
451 if (OidIsValid(commutatorId) &&
452 !pg_oper_ownercheck(commutatorId, GetUserId()))
453 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
454 NameListToString(commutatorName));
457 * self-linkage to this operator; will fix below. Note that only
458 * self-linkage for commutation makes sense.
460 if (!OidIsValid(commutatorId))
461 selfCommutator = true;
464 commutatorId = InvalidOid;
468 /* negator has same arg types */
469 negatorId = get_other_operator(negatorName,
470 leftTypeId, rightTypeId,
471 operatorName, operatorNamespace,
472 leftTypeId, rightTypeId,
475 /* Permission check: must own other operator */
476 if (OidIsValid(negatorId) &&
477 !pg_oper_ownercheck(negatorId, GetUserId()))
478 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
479 NameListToString(negatorName));
482 negatorId = InvalidOid;
485 * set up values in the operator tuple
488 for (i = 0; i < Natts_pg_operator; ++i)
490 values[i] = (Datum) NULL;
495 namestrcpy(&oname, operatorName);
496 values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
497 values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
498 values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
499 values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l');
500 values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
501 values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
502 values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
503 values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
504 values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType);
505 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId);
506 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId);
507 values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
508 values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
509 values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
511 pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
514 * If we are replacing an operator shell, update; else insert
516 if (operatorObjectId)
518 tup = SearchSysCacheCopy1(OPEROID,
519 ObjectIdGetDatum(operatorObjectId));
520 if (!HeapTupleIsValid(tup))
521 elog(ERROR, "cache lookup failed for operator %u",
524 tup = heap_modify_tuple(tup,
525 RelationGetDescr(pg_operator_desc),
530 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
534 tupDesc = pg_operator_desc->rd_att;
535 tup = heap_form_tuple(tupDesc, values, nulls);
537 operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
540 /* Must update the indexes in either case */
541 CatalogUpdateIndexes(pg_operator_desc, tup);
543 /* Add dependencies for the entry */
544 makeOperatorDependencies(tup);
546 /* Post creation hook for new operator */
547 InvokeObjectAccessHook(OAT_POST_CREATE,
548 OperatorRelationId, operatorObjectId, 0, NULL);
550 heap_close(pg_operator_desc, RowExclusiveLock);
553 * If a commutator and/or negator link is provided, update the other
554 * operator(s) to point at this one, if they don't already have a link.
555 * This supports an alternative style of operator definition wherein the
556 * user first defines one operator without giving negator or commutator,
557 * then defines the other operator of the pair with the proper commutator
558 * or negator attribute. That style doesn't require creation of a shell,
559 * and it's the only style that worked right before Postgres version 6.5.
560 * This code also takes care of the situation where the new operator is
561 * its own commutator.
564 commutatorId = operatorObjectId;
566 if (OidIsValid(commutatorId) || OidIsValid(negatorId))
567 OperatorUpd(operatorObjectId, commutatorId, negatorId);
569 return operatorObjectId;
573 * Try to lookup another operator (commutator, etc)
575 * If not found, check to see if it is exactly the operator we are trying
576 * to define; if so, return InvalidOid. (Note that this case is only
577 * sensible for a commutator, so we error out otherwise.) If it is not
578 * the same operator, create a shell operator.
581 get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
582 const char *operatorName, Oid operatorNamespace,
583 Oid leftTypeId, Oid rightTypeId, bool isCommutator)
591 other_oid = OperatorLookup(otherOp,
596 if (OidIsValid(other_oid))
598 /* other op already in catalogs */
602 otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
605 if (strcmp(otherName, operatorName) == 0 &&
606 otherNamespace == operatorNamespace &&
607 otherLeftTypeId == leftTypeId &&
608 otherRightTypeId == rightTypeId)
611 * self-linkage to this operator; caller will fix later. Note that
612 * only self-linkage for commutation makes sense.
616 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
617 errmsg("operator cannot be its own negator or sort operator")));
621 /* not in catalogs, different from operator, so make shell */
623 aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
625 if (aclresult != ACLCHECK_OK)
626 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
627 get_namespace_name(otherNamespace));
629 other_oid = OperatorShellMake(otherName,
639 * For a given operator, look up its negator and commutator operators.
640 * If they are defined, but their negator and commutator fields
641 * (respectively) are empty, then use the new operator for neg or comm.
642 * This solves a problem for users who need to insert two new operators
643 * which are the negator or commutator of each other.
646 OperatorUpd(Oid baseId, Oid commId, Oid negId)
649 Relation pg_operator_desc;
651 bool nulls[Natts_pg_operator];
652 bool replaces[Natts_pg_operator];
653 Datum values[Natts_pg_operator];
655 for (i = 0; i < Natts_pg_operator; ++i)
657 values[i] = (Datum) 0;
663 * check and update the commutator & negator, if necessary
665 * We need a CommandCounterIncrement here in case of a self-commutator
666 * operator: we'll need to update the tuple that we just inserted.
668 CommandCounterIncrement();
670 pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
672 tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId));
675 * if the commutator and negator are the same operator, do one update. XXX
676 * this is probably useless code --- I doubt it ever makes sense for
677 * commutator and negator to be the same thing...
681 if (HeapTupleIsValid(tup))
683 Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
685 if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate))
687 if (!OidIsValid(t->oprnegate))
689 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
690 replaces[Anum_pg_operator_oprnegate - 1] = true;
693 if (!OidIsValid(t->oprcom))
695 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
696 replaces[Anum_pg_operator_oprcom - 1] = true;
699 tup = heap_modify_tuple(tup,
700 RelationGetDescr(pg_operator_desc),
705 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
707 CatalogUpdateIndexes(pg_operator_desc, tup);
711 heap_close(pg_operator_desc, RowExclusiveLock);
716 /* if commutator and negator are different, do two updates */
718 if (HeapTupleIsValid(tup) &&
719 !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom)))
721 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
722 replaces[Anum_pg_operator_oprcom - 1] = true;
724 tup = heap_modify_tuple(tup,
725 RelationGetDescr(pg_operator_desc),
730 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
732 CatalogUpdateIndexes(pg_operator_desc, tup);
734 values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
735 replaces[Anum_pg_operator_oprcom - 1] = false;
738 /* check and update the negator, if necessary */
740 tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId));
742 if (HeapTupleIsValid(tup) &&
743 !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
745 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
746 replaces[Anum_pg_operator_oprnegate - 1] = true;
748 tup = heap_modify_tuple(tup,
749 RelationGetDescr(pg_operator_desc),
754 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
756 CatalogUpdateIndexes(pg_operator_desc, tup);
759 heap_close(pg_operator_desc, RowExclusiveLock);
763 * Create dependencies for a new operator (either a freshly inserted
764 * complete operator, a new shell operator, or a just-updated shell).
766 * NB: the OidIsValid tests in this routine are necessary, in case
767 * the given operator is a shell.
770 makeOperatorDependencies(HeapTuple tuple)
772 Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
773 ObjectAddress myself,
776 myself.classId = OperatorRelationId;
777 myself.objectId = HeapTupleGetOid(tuple);
778 myself.objectSubId = 0;
781 * In case we are updating a shell, delete any existing entries, except
782 * for extension membership which should remain the same.
784 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
785 deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
787 /* Dependency on namespace */
788 if (OidIsValid(oper->oprnamespace))
790 referenced.classId = NamespaceRelationId;
791 referenced.objectId = oper->oprnamespace;
792 referenced.objectSubId = 0;
793 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
796 /* Dependency on left type */
797 if (OidIsValid(oper->oprleft))
799 referenced.classId = TypeRelationId;
800 referenced.objectId = oper->oprleft;
801 referenced.objectSubId = 0;
802 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
805 /* Dependency on right type */
806 if (OidIsValid(oper->oprright))
808 referenced.classId = TypeRelationId;
809 referenced.objectId = oper->oprright;
810 referenced.objectSubId = 0;
811 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
814 /* Dependency on result type */
815 if (OidIsValid(oper->oprresult))
817 referenced.classId = TypeRelationId;
818 referenced.objectId = oper->oprresult;
819 referenced.objectSubId = 0;
820 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
824 * NOTE: we do not consider the operator to depend on the associated
825 * operators oprcom and oprnegate. We would not want to delete this
826 * operator if those go away, but only reset the link fields; which is not
827 * a function that the dependency code can presently handle. (Something
828 * could perhaps be done with objectSubId though.) For now, it's okay to
829 * let those links dangle if a referenced operator is removed.
832 /* Dependency on implementation function */
833 if (OidIsValid(oper->oprcode))
835 referenced.classId = ProcedureRelationId;
836 referenced.objectId = oper->oprcode;
837 referenced.objectSubId = 0;
838 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
841 /* Dependency on restriction selectivity function */
842 if (OidIsValid(oper->oprrest))
844 referenced.classId = ProcedureRelationId;
845 referenced.objectId = oper->oprrest;
846 referenced.objectSubId = 0;
847 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
850 /* Dependency on join selectivity function */
851 if (OidIsValid(oper->oprjoin))
853 referenced.classId = ProcedureRelationId;
854 referenced.objectId = oper->oprjoin;
855 referenced.objectSubId = 0;
856 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
859 /* Dependency on owner */
860 recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
863 /* Dependency on extension */
864 recordDependencyOnCurrentExtension(&myself, true);