1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_operator relation
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.76 2002/08/22 00:01:41 tgl Exp $
14 * these routines moved here from commands/define.c and somewhat cleaned up.
16 *-------------------------------------------------------------------------
20 #include "access/heapam.h"
21 #include "catalog/catname.h"
22 #include "catalog/dependency.h"
23 #include "catalog/indexing.h"
24 #include "catalog/namespace.h"
25 #include "catalog/pg_operator.h"
26 #include "catalog/pg_type.h"
27 #include "miscadmin.h"
28 #include "parser/parse_func.h"
29 #include "parser/parse_oper.h"
30 #include "utils/acl.h"
31 #include "utils/builtins.h"
32 #include "utils/lsyscache.h"
33 #include "utils/syscache.h"
36 static Oid OperatorGet(const char *operatorName,
37 Oid operatorNamespace,
42 static Oid OperatorLookup(List *operatorName,
47 static Oid OperatorShellMake(const char *operatorName,
48 Oid operatorNamespace,
52 static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
54 static Oid get_other_operator(List *otherOp,
55 Oid otherLeftTypeId, Oid otherRightTypeId,
56 const char *operatorName, Oid operatorNamespace,
57 Oid leftTypeId, Oid rightTypeId,
60 static void makeOperatorDependencies(HeapTuple tuple, Oid pg_operator_relid);
64 * Check whether a proposed operator name is legal
66 * This had better match the behavior of parser/scan.l!
68 * We need this because the parser is not smart enough to check that
69 * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
70 * are operator names rather than some other lexical entity.
73 validOperatorName(const char *name)
75 size_t len = strlen(name);
77 /* Can't be empty or too long */
78 if (len == 0 || len >= NAMEDATALEN)
81 /* Can't contain any invalid characters */
82 /* Test string here should match op_chars in scan.l */
83 if (strspn(name, "~!@#^&|`?$+-*/%<>=") != len)
86 /* Can't contain slash-star or dash-dash (comment starts) */
87 if (strstr(name, "/*") || strstr(name, "--"))
91 * For SQL92 compatibility, '+' and '-' cannot be the last char of a
92 * multi-char operator unless the operator contains chars that are not
93 * in SQL92 operators. The idea is to lex '=-' as two operators, but
94 * not to forbid operator names like '?-' that could not be sequences
98 (name[len - 1] == '+' ||
99 name[len - 1] == '-'))
103 for (ic = len - 2; ic >= 0; ic--)
105 if (strchr("~!@#^&|`?$%", name[ic]))
109 return false; /* nope, not valid */
112 /* != isn't valid either, because parser will convert it to <> */
113 if (strcmp(name, "!=") == 0)
123 * finds an operator given an exact specification (name, namespace,
124 * left and right type IDs).
126 * *defined is set TRUE if defined (not a shell)
129 OperatorGet(const char *operatorName,
130 Oid operatorNamespace,
136 Oid operatorObjectId;
138 tup = SearchSysCache(OPERNAMENSP,
139 PointerGetDatum(operatorName),
140 ObjectIdGetDatum(leftObjectId),
141 ObjectIdGetDatum(rightObjectId),
142 ObjectIdGetDatum(operatorNamespace));
143 if (HeapTupleIsValid(tup))
145 RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
147 operatorObjectId = HeapTupleGetOid(tup);
148 *defined = RegProcedureIsValid(oprcode);
149 ReleaseSysCache(tup);
153 operatorObjectId = InvalidOid;
157 return operatorObjectId;
163 * looks up an operator given a possibly-qualified name and
164 * left and right type IDs.
166 * *defined is set TRUE if defined (not a shell)
169 OperatorLookup(List *operatorName,
174 Oid operatorObjectId;
175 RegProcedure oprcode;
177 operatorObjectId = LookupOperName(operatorName, leftObjectId,
179 if (!OidIsValid(operatorObjectId))
185 oprcode = get_opcode(operatorObjectId);
186 *defined = RegProcedureIsValid(oprcode);
188 return operatorObjectId;
194 * Make a "shell" entry for a not-yet-existing operator.
197 OperatorShellMake(const char *operatorName,
198 Oid operatorNamespace,
202 Relation pg_operator_desc;
203 Oid operatorObjectId;
206 Datum values[Natts_pg_operator];
207 char nulls[Natts_pg_operator];
212 * validate operator name
214 if (!validOperatorName(operatorName))
215 elog(ERROR, "\"%s\" is not a valid operator name", operatorName);
218 * initialize our *nulls and *values arrays
220 for (i = 0; i < Natts_pg_operator; ++i)
223 values[i] = (Datum) NULL; /* redundant, but safe */
227 * initialize values[] with the operator name and input data types.
228 * Note that oprcode is set to InvalidOid, indicating it's a shell.
231 namestrcpy(&oname, operatorName);
232 values[i++] = NameGetDatum(&oname); /* oprname */
233 values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
234 values[i++] = Int32GetDatum(GetUserId()); /* oprowner */
235 values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
236 values[i++] = BoolGetDatum(false); /* oprcanhash */
237 values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
238 values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
239 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprresult */
240 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcom */
241 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprnegate */
242 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprlsortop */
243 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrsortop */
244 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprltcmpop */
245 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprgtcmpop */
246 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcode */
247 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrest */
248 values[i++] = ObjectIdGetDatum(InvalidOid); /* oprjoin */
253 pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
254 tupDesc = pg_operator_desc->rd_att;
257 * create a new operator tuple
259 tup = heap_formtuple(tupDesc, values, nulls);
262 * insert our "shell" operator tuple
264 operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
266 CatalogUpdateIndexes(pg_operator_desc, tup);
268 /* Add dependencies for the entry */
269 makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc));
274 * close the operator relation and return the oid.
276 heap_close(pg_operator_desc, RowExclusiveLock);
278 return operatorObjectId;
284 * "X" indicates an optional argument (i.e. one that can be NULL or 0)
285 * operatorName name for new operator
286 * operatorNamespace namespace for new operator
287 * leftTypeId X left type ID
288 * rightTypeId X right type ID
289 * procedureName procedure for operator
290 * commutatorName X commutator operator
291 * negatorName X negator operator
292 * restrictionName X restriction sel. procedure
293 * joinName X join sel. procedure
294 * canHash hash join can be used with this operator
295 * leftSortName X left sort operator (for merge join)
296 * rightSortName X right sort operator (for merge join)
297 * ltCompareName X L<R compare operator (for merge join)
298 * gtCompareName X L>R compare operator (for merge join)
300 * This routine gets complicated because it allows the user to
301 * specify operators that do not exist. For example, if operator
302 * "op" is being defined, the negator operator "negop" and the
303 * commutator "commop" can also be defined without specifying
304 * any information other than their names. Since in order to
305 * add "op" to the PG_OPERATOR catalog, all the Oid's for these
306 * operators must be placed in the fields of "op", a forward
307 * declaration is done on the commutator and negator operators.
308 * This is called creating a shell, and its main effect is to
309 * create a tuple in the PG_OPERATOR catalog with minimal
310 * information about the operator (just its name and types).
311 * Forward declaration is used only for this purpose, it is
312 * not available to the user as it is for type definition.
316 * check if operator already defined
317 * if so, but oprcode is null, save the Oid -- we are filling in a shell
319 * get the attribute types from relation descriptor for pg_operator
320 * assign values to the fields of the operator:
322 * owner id (simply the user id of the caller)
323 * operator "kind" either "b" for binary or "l" for left unary
325 * leftTypeObjectId -- type must already be defined
326 * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified
327 * resultType -- defer this, since it must be determined from
328 * the pg_procedure catalog
329 * commutatorObjectId -- if this is NULL, enter ObjectId=0
330 * else if this already exists, enter its ObjectId
331 * else if this does not yet exist, and is not
332 * the same as the main operatorName, then create
333 * a shell and enter the new ObjectId
334 * else if this does not exist but IS the same
335 * name & types as the main operator, set the ObjectId=0.
336 * (We are creating a self-commutating operator.)
337 * The link will be fixed later by OperatorUpd.
338 * negatorObjectId -- same as for commutatorObjectId
339 * leftSortObjectId -- same as for commutatorObjectId
340 * rightSortObjectId -- same as for commutatorObjectId
341 * operatorProcedure -- must access the pg_procedure catalog to get the
342 * ObjectId of the procedure that actually does the operator
343 * actions this is required. Do a lookup to find out the
344 * return type of the procedure
345 * restrictionProcedure -- must access the pg_procedure catalog to get
346 * the ObjectId but this is optional
347 * joinProcedure -- same as restrictionProcedure
348 * now either insert or replace the operator into the pg_operator catalog
349 * if the operator shell is being filled in
350 * access the catalog in order to get a valid buffer
351 * create a tuple using ModifyHeapTuple
352 * get the t_self from the modified tuple and call RelationReplaceHeapTuple
353 * else if a new operator is being created
354 * create a tuple using heap_formtuple
355 * call simple_heap_insert
358 OperatorCreate(const char *operatorName,
359 Oid operatorNamespace,
363 List *commutatorName,
365 List *restrictionName,
373 Relation pg_operator_desc;
375 char nulls[Natts_pg_operator];
376 char replaces[Natts_pg_operator];
377 Datum values[Natts_pg_operator];
378 Oid operatorObjectId;
379 bool operatorAlreadyDefined;
390 bool selfCommutator = false;
391 Oid typeId[FUNC_MAX_ARGS];
400 if (!validOperatorName(operatorName))
401 elog(ERROR, "\"%s\" is not a valid operator name", operatorName);
403 if (!OidIsValid(leftTypeId) && !OidIsValid(rightTypeId))
404 elog(ERROR, "at least one of leftarg or rightarg must be specified");
406 if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
408 /* If it's not a binary op, these things mustn't be set: */
410 elog(ERROR, "only binary operators can have commutators");
412 elog(ERROR, "only binary operators can have join selectivity");
414 elog(ERROR, "only binary operators can hash");
415 if (leftSortName || rightSortName || ltCompareName || gtCompareName)
416 elog(ERROR, "only binary operators can mergejoin");
419 operatorObjectId = OperatorGet(operatorName,
423 &operatorAlreadyDefined);
425 if (operatorAlreadyDefined)
426 elog(ERROR, "OperatorDef: operator \"%s\" already defined",
430 * At this point, if operatorObjectId is not InvalidOid then we are
431 * filling in a previously-created shell.
435 * Look up registered procedures -- find the return type of
436 * procedureName to place in "result" field. Do this before shells are
437 * created so we don't have to worry about deleting them later.
439 MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid));
440 if (!OidIsValid(leftTypeId))
442 typeId[0] = rightTypeId;
445 else if (!OidIsValid(rightTypeId))
447 typeId[0] = leftTypeId;
452 typeId[0] = leftTypeId;
453 typeId[1] = rightTypeId;
456 procOid = LookupFuncName(procedureName, nargs, typeId);
457 if (!OidIsValid(procOid))
458 func_error("OperatorDef", procedureName, nargs, typeId, NULL);
459 operResultType = get_func_rettype(procOid);
462 * find restriction estimator
466 MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid));
467 typeId[0] = INTERNALOID; /* Query */
468 typeId[1] = OIDOID; /* operator OID */
469 typeId[2] = INTERNALOID; /* args list */
470 typeId[3] = INT4OID; /* varRelid */
472 restOid = LookupFuncName(restrictionName, 4, typeId);
473 if (!OidIsValid(restOid))
474 func_error("OperatorDef", restrictionName, 4, typeId, NULL);
477 restOid = InvalidOid;
480 * find join estimator
484 MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid));
485 typeId[0] = INTERNALOID; /* Query */
486 typeId[1] = OIDOID; /* operator OID */
487 typeId[2] = INTERNALOID; /* args list */
489 joinOid = LookupFuncName(joinName, 3, typeId);
490 if (!OidIsValid(joinOid))
491 func_error("OperatorDef", joinName, 3, typeId, NULL);
494 joinOid = InvalidOid;
497 * set up values in the operator tuple
500 for (i = 0; i < Natts_pg_operator; ++i)
502 values[i] = (Datum) NULL;
508 namestrcpy(&oname, operatorName);
509 values[i++] = NameGetDatum(&oname); /* oprname */
510 values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
511 values[i++] = Int32GetDatum(GetUserId()); /* oprowner */
512 values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
513 values[i++] = BoolGetDatum(canHash); /* oprcanhash */
514 values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
515 values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
516 values[i++] = ObjectIdGetDatum(operResultType); /* oprresult */
519 * Set up the other operators. If they do not currently exist, create
520 * shells in order to get ObjectId's.
525 /* commutator has reversed arg types */
526 commutatorId = get_other_operator(commutatorName,
527 rightTypeId, leftTypeId,
528 operatorName, operatorNamespace,
529 leftTypeId, rightTypeId,
532 * self-linkage to this operator; will fix below. Note
533 * that only self-linkage for commutation makes sense.
535 if (!OidIsValid(commutatorId))
536 selfCommutator = true;
539 commutatorId = InvalidOid;
540 values[i++] = ObjectIdGetDatum(commutatorId); /* oprcom */
544 /* negator has same arg types */
545 negatorId = get_other_operator(negatorName,
546 leftTypeId, rightTypeId,
547 operatorName, operatorNamespace,
548 leftTypeId, rightTypeId,
552 negatorId = InvalidOid;
553 values[i++] = ObjectIdGetDatum(negatorId); /* oprnegate */
557 /* left sort op takes left-side data type */
558 leftSortId = get_other_operator(leftSortName,
559 leftTypeId, leftTypeId,
560 operatorName, operatorNamespace,
561 leftTypeId, rightTypeId,
565 leftSortId = InvalidOid;
566 values[i++] = ObjectIdGetDatum(leftSortId); /* oprlsortop */
570 /* right sort op takes right-side data type */
571 rightSortId = get_other_operator(rightSortName,
572 rightTypeId, rightTypeId,
573 operatorName, operatorNamespace,
574 leftTypeId, rightTypeId,
578 rightSortId = InvalidOid;
579 values[i++] = ObjectIdGetDatum(rightSortId); /* oprrsortop */
583 /* comparator has same arg types */
584 ltCompareId = get_other_operator(ltCompareName,
585 leftTypeId, rightTypeId,
586 operatorName, operatorNamespace,
587 leftTypeId, rightTypeId,
591 ltCompareId = InvalidOid;
592 values[i++] = ObjectIdGetDatum(ltCompareId); /* oprltcmpop */
596 /* comparator has same arg types */
597 gtCompareId = get_other_operator(gtCompareName,
598 leftTypeId, rightTypeId,
599 operatorName, operatorNamespace,
600 leftTypeId, rightTypeId,
604 gtCompareId = InvalidOid;
605 values[i++] = ObjectIdGetDatum(gtCompareId); /* oprgtcmpop */
607 values[i++] = ObjectIdGetDatum(procOid); /* oprcode */
608 values[i++] = ObjectIdGetDatum(restOid); /* oprrest */
609 values[i++] = ObjectIdGetDatum(joinOid); /* oprjoin */
611 pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
614 * If we are adding to an operator shell, update; else insert
616 if (operatorObjectId)
618 tup = SearchSysCacheCopy(OPEROID,
619 ObjectIdGetDatum(operatorObjectId),
621 if (!HeapTupleIsValid(tup))
622 elog(ERROR, "OperatorDef: operator %u not found",
625 tup = heap_modifytuple(tup,
631 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
635 tupDesc = pg_operator_desc->rd_att;
636 tup = heap_formtuple(tupDesc, values, nulls);
638 operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
641 /* Must update the indexes in either case */
642 CatalogUpdateIndexes(pg_operator_desc, tup);
644 /* Add dependencies for the entry */
645 makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc));
647 heap_close(pg_operator_desc, RowExclusiveLock);
650 * If a commutator and/or negator link is provided, update the other
651 * operator(s) to point at this one, if they don't already have a
652 * link. This supports an alternate style of operator definition
653 * wherein the user first defines one operator without giving negator
654 * or commutator, then defines the other operator of the pair with the
655 * proper commutator or negator attribute. That style doesn't require
656 * creation of a shell, and it's the only style that worked right
657 * before Postgres version 6.5. This code also takes care of the
658 * situation where the new operator is its own commutator.
661 commutatorId = operatorObjectId;
663 if (OidIsValid(commutatorId) || OidIsValid(negatorId))
664 OperatorUpd(operatorObjectId, commutatorId, negatorId);
668 * Try to lookup another operator (commutator, etc)
670 * If not found, check to see if it is exactly the operator we are trying
671 * to define; if so, return InvalidOid. (Note that this case is only
672 * sensible for a commutator, so we error out otherwise.) If it is not
673 * the same operator, create a shell operator.
676 get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
677 const char *operatorName, Oid operatorNamespace,
678 Oid leftTypeId, Oid rightTypeId, bool isCommutator)
686 other_oid = OperatorLookup(otherOp,
691 if (OidIsValid(other_oid))
693 /* other op already in catalogs */
697 otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
700 if (strcmp(otherName, operatorName) == 0 &&
701 otherNamespace == operatorNamespace &&
702 otherLeftTypeId == leftTypeId &&
703 otherRightTypeId == rightTypeId)
706 * self-linkage to this operator; caller will fix later. Note
707 * that only self-linkage for commutation makes sense.
710 elog(ERROR, "operator cannot be its own negator or sort operator");
714 /* not in catalogs, different from operator, so make shell */
716 aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
718 if (aclresult != ACLCHECK_OK)
719 aclcheck_error(aclresult, get_namespace_name(otherNamespace));
721 other_oid = OperatorShellMake(otherName,
725 if (!OidIsValid(other_oid))
727 "OperatorDef: can't create operator shell \"%s\"",
728 NameListToString(otherOp));
735 * For a given operator, look up its negator and commutator operators.
736 * If they are defined, but their negator and commutator fields
737 * (respectively) are empty, then use the new operator for neg or comm.
738 * This solves a problem for users who need to insert two new operators
739 * which are the negator or commutator of each other.
742 OperatorUpd(Oid baseId, Oid commId, Oid negId)
745 Relation pg_operator_desc;
747 char nulls[Natts_pg_operator];
748 char replaces[Natts_pg_operator];
749 Datum values[Natts_pg_operator];
751 for (i = 0; i < Natts_pg_operator; ++i)
753 values[i] = (Datum) 0;
759 * check and update the commutator & negator, if necessary
761 * First make sure we can see them...
763 CommandCounterIncrement();
765 pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
767 tup = SearchSysCacheCopy(OPEROID,
768 ObjectIdGetDatum(commId),
772 * if the commutator and negator are the same operator, do one update.
773 * XXX this is probably useless code --- I doubt it ever makes sense
774 * for commutator and negator to be the same thing...
778 if (HeapTupleIsValid(tup))
780 Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
782 if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate))
784 if (!OidIsValid(t->oprnegate))
786 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
787 replaces[Anum_pg_operator_oprnegate - 1] = 'r';
790 if (!OidIsValid(t->oprcom))
792 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
793 replaces[Anum_pg_operator_oprcom - 1] = 'r';
796 tup = heap_modifytuple(tup,
802 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
804 CatalogUpdateIndexes(pg_operator_desc, tup);
808 heap_close(pg_operator_desc, RowExclusiveLock);
813 /* if commutator and negator are different, do two updates */
815 if (HeapTupleIsValid(tup) &&
816 !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom)))
818 values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
819 replaces[Anum_pg_operator_oprcom - 1] = 'r';
821 tup = heap_modifytuple(tup,
827 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
829 CatalogUpdateIndexes(pg_operator_desc, tup);
831 values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
832 replaces[Anum_pg_operator_oprcom - 1] = ' ';
835 /* check and update the negator, if necessary */
837 tup = SearchSysCacheCopy(OPEROID,
838 ObjectIdGetDatum(negId),
841 if (HeapTupleIsValid(tup) &&
842 !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
844 values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
845 replaces[Anum_pg_operator_oprnegate - 1] = 'r';
847 tup = heap_modifytuple(tup,
853 simple_heap_update(pg_operator_desc, &tup->t_self, tup);
855 CatalogUpdateIndexes(pg_operator_desc, tup);
858 heap_close(pg_operator_desc, RowExclusiveLock);
862 * Create dependencies for a new operator (either a freshly inserted
863 * complete operator, a new shell operator, or a just-updated shell).
865 * NB: the OidIsValid tests in this routine are necessary, in case
866 * the given operator is a shell.
869 makeOperatorDependencies(HeapTuple tuple, Oid pg_operator_relid)
871 Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
872 ObjectAddress myself,
875 myself.classId = pg_operator_relid;
876 myself.objectId = HeapTupleGetOid(tuple);
877 myself.objectSubId = 0;
879 /* In case we are updating a shell, delete any existing entries */
880 deleteDependencyRecordsFor(myself.classId, myself.objectId);
882 /* Dependency on namespace */
883 if (OidIsValid(oper->oprnamespace))
885 referenced.classId = get_system_catalog_relid(NamespaceRelationName);
886 referenced.objectId = oper->oprnamespace;
887 referenced.objectSubId = 0;
888 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
891 /* Dependency on left type */
892 if (OidIsValid(oper->oprleft))
894 referenced.classId = RelOid_pg_type;
895 referenced.objectId = oper->oprleft;
896 referenced.objectSubId = 0;
897 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
900 /* Dependency on right type */
901 if (OidIsValid(oper->oprright))
903 referenced.classId = RelOid_pg_type;
904 referenced.objectId = oper->oprright;
905 referenced.objectSubId = 0;
906 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
909 /* Dependency on result type */
910 if (OidIsValid(oper->oprresult))
912 referenced.classId = RelOid_pg_type;
913 referenced.objectId = oper->oprresult;
914 referenced.objectSubId = 0;
915 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
919 * NOTE: we do not consider the operator to depend on the associated
920 * operators oprcom, oprnegate, oprlsortop, oprrsortop, oprltcmpop,
921 * oprgtcmpop. We would not want to delete this operator if those
922 * go away, but only reset the link fields; which is not a function
923 * that the dependency code can presently handle. (Something could
924 * perhaps be done with objectSubId though.) For now, it's okay to
925 * let those links dangle if a referenced operator is removed.
928 /* Dependency on implementation function */
929 if (OidIsValid(oper->oprcode))
931 referenced.classId = RelOid_pg_proc;
932 referenced.objectId = oper->oprcode;
933 referenced.objectSubId = 0;
934 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
937 /* Dependency on restriction selectivity function */
938 if (OidIsValid(oper->oprrest))
940 referenced.classId = RelOid_pg_proc;
941 referenced.objectId = oper->oprrest;
942 referenced.objectSubId = 0;
943 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
946 /* Dependency on join selectivity function */
947 if (OidIsValid(oper->oprjoin))
949 referenced.classId = RelOid_pg_proc;
950 referenced.objectId = oper->oprjoin;
951 referenced.objectSubId = 0;
952 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);