1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_operator relation
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
14 * these routines moved here from commands/define.c and somewhat cleaned up.
16 *-------------------------------------------------------------------------
20 #include "access/heapam.h"
21 #include "access/xact.h"
22 #include "catalog/dependency.h"
23 #include "catalog/indexing.h"
24 #include "catalog/namespace.h"
25 #include "catalog/pg_namespace.h"
26 #include "catalog/pg_operator.h"
27 #include "catalog/pg_proc.h"
28 #include "catalog/pg_type.h"
29 #include "miscadmin.h"
30 #include "parser/parse_oper.h"
31 #include "utils/acl.h"
32 #include "utils/builtins.h"
33 #include "utils/lsyscache.h"
34 #include "utils/rel.h"
35 #include "utils/syscache.h"
38 static Oid
OperatorGet(const char *operatorName
,
39 Oid operatorNamespace
,
44 static Oid
OperatorLookup(List
*operatorName
,
49 static Oid
OperatorShellMake(const char *operatorName
,
50 Oid operatorNamespace
,
54 static void OperatorUpd(Oid baseId
, Oid commId
, Oid negId
);
56 static Oid
get_other_operator(List
*otherOp
,
57 Oid otherLeftTypeId
, Oid otherRightTypeId
,
58 const char *operatorName
, Oid operatorNamespace
,
59 Oid leftTypeId
, Oid rightTypeId
,
62 static void makeOperatorDependencies(HeapTuple tuple
);
66 * Check whether a proposed operator name is legal
68 * This had better match the behavior of parser/scan.l!
70 * We need this because the parser is not smart enough to check that
71 * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
72 * are operator names rather than some other lexical entity.
75 validOperatorName(const char *name
)
77 size_t len
= strlen(name
);
79 /* Can't be empty or too long */
80 if (len
== 0 || len
>= NAMEDATALEN
)
83 /* Can't contain any invalid characters */
84 /* Test string here should match op_chars in scan.l */
85 if (strspn(name
, "~!@#^&|`?+-*/%<>=") != len
)
88 /* Can't contain slash-star or dash-dash (comment starts) */
89 if (strstr(name
, "/*") || strstr(name
, "--"))
93 * For SQL92 compatibility, '+' and '-' cannot be the last char of a
94 * multi-char operator unless the operator contains chars that are not in
95 * SQL92 operators. The idea is to lex '=-' as two operators, but not to
96 * forbid operator names like '?-' that could not be sequences of SQL92
100 (name
[len
- 1] == '+' ||
101 name
[len
- 1] == '-'))
105 for (ic
= len
- 2; ic
>= 0; ic
--)
107 if (strchr("~!@#^&|`?%", name
[ic
]))
111 return false; /* nope, not valid */
114 /* != isn't valid either, because parser will convert it to <> */
115 if (strcmp(name
, "!=") == 0)
125 * finds an operator given an exact specification (name, namespace,
126 * left and right type IDs).
128 * *defined is set TRUE if defined (not a shell)
131 OperatorGet(const char *operatorName
,
132 Oid operatorNamespace
,
138 Oid operatorObjectId
;
140 tup
= SearchSysCache(OPERNAMENSP
,
141 PointerGetDatum(operatorName
),
142 ObjectIdGetDatum(leftObjectId
),
143 ObjectIdGetDatum(rightObjectId
),
144 ObjectIdGetDatum(operatorNamespace
));
145 if (HeapTupleIsValid(tup
))
147 RegProcedure oprcode
= ((Form_pg_operator
) GETSTRUCT(tup
))->oprcode
;
149 operatorObjectId
= HeapTupleGetOid(tup
);
150 *defined
= RegProcedureIsValid(oprcode
);
151 ReleaseSysCache(tup
);
155 operatorObjectId
= InvalidOid
;
159 return operatorObjectId
;
165 * looks up an operator given a possibly-qualified name and
166 * left and right type IDs.
168 * *defined is set TRUE if defined (not a shell)
171 OperatorLookup(List
*operatorName
,
176 Oid operatorObjectId
;
177 RegProcedure oprcode
;
179 operatorObjectId
= LookupOperName(NULL
, operatorName
,
180 leftObjectId
, rightObjectId
,
182 if (!OidIsValid(operatorObjectId
))
188 oprcode
= get_opcode(operatorObjectId
);
189 *defined
= RegProcedureIsValid(oprcode
);
191 return operatorObjectId
;
197 * Make a "shell" entry for a not-yet-existing operator.
200 OperatorShellMake(const char *operatorName
,
201 Oid operatorNamespace
,
205 Relation pg_operator_desc
;
206 Oid operatorObjectId
;
209 Datum values
[Natts_pg_operator
];
210 char nulls
[Natts_pg_operator
];
215 * validate operator name
217 if (!validOperatorName(operatorName
))
219 (errcode(ERRCODE_INVALID_NAME
),
220 errmsg("\"%s\" is not a valid operator name",
224 * initialize our *nulls and *values arrays
226 for (i
= 0; i
< Natts_pg_operator
; ++i
)
229 values
[i
] = (Datum
) NULL
; /* redundant, but safe */
233 * initialize values[] with the operator name and input data types. Note
234 * that oprcode is set to InvalidOid, indicating it's a shell.
237 namestrcpy(&oname
, operatorName
);
238 values
[i
++] = NameGetDatum(&oname
); /* oprname */
239 values
[i
++] = ObjectIdGetDatum(operatorNamespace
); /* oprnamespace */
240 values
[i
++] = ObjectIdGetDatum(GetUserId()); /* oprowner */
241 values
[i
++] = CharGetDatum(leftTypeId
? (rightTypeId
? 'b' : 'r') : 'l'); /* oprkind */
242 values
[i
++] = BoolGetDatum(false); /* oprcanmerge */
243 values
[i
++] = BoolGetDatum(false); /* oprcanhash */
244 values
[i
++] = ObjectIdGetDatum(leftTypeId
); /* oprleft */
245 values
[i
++] = ObjectIdGetDatum(rightTypeId
); /* oprright */
246 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprresult */
247 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprcom */
248 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprnegate */
249 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprcode */
250 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprrest */
251 values
[i
++] = ObjectIdGetDatum(InvalidOid
); /* oprjoin */
256 pg_operator_desc
= heap_open(OperatorRelationId
, RowExclusiveLock
);
257 tupDesc
= pg_operator_desc
->rd_att
;
260 * create a new operator tuple
262 tup
= heap_formtuple(tupDesc
, values
, nulls
);
265 * insert our "shell" operator tuple
267 operatorObjectId
= simple_heap_insert(pg_operator_desc
, tup
);
269 CatalogUpdateIndexes(pg_operator_desc
, tup
);
271 /* Add dependencies for the entry */
272 makeOperatorDependencies(tup
);
277 * Make sure the tuple is visible for subsequent lookups/updates.
279 CommandCounterIncrement();
282 * close the operator relation and return the oid.
284 heap_close(pg_operator_desc
, RowExclusiveLock
);
286 return operatorObjectId
;
292 * "X" indicates an optional argument (i.e. one that can be NULL or 0)
293 * operatorName name for new operator
294 * operatorNamespace namespace for new operator
295 * leftTypeId X left type ID
296 * rightTypeId X right type ID
297 * procedureId procedure ID for operator
298 * commutatorName X commutator operator
299 * negatorName X negator operator
300 * restrictionId X restriction selectivity procedure ID
301 * joinId X join selectivity procedure ID
302 * canMerge merge join can be used with this operator
303 * canHash hash join can be used with this operator
305 * The caller should have validated properties and permissions for the
306 * objects passed as OID references. We must handle the commutator and
307 * negator operator references specially, however, since those need not
310 * This routine gets complicated because it allows the user to
311 * specify operators that do not exist. For example, if operator
312 * "op" is being defined, the negator operator "negop" and the
313 * commutator "commop" can also be defined without specifying
314 * any information other than their names. Since in order to
315 * add "op" to the PG_OPERATOR catalog, all the Oid's for these
316 * operators must be placed in the fields of "op", a forward
317 * declaration is done on the commutator and negator operators.
318 * This is called creating a shell, and its main effect is to
319 * create a tuple in the PG_OPERATOR catalog with minimal
320 * information about the operator (just its name and types).
321 * Forward declaration is used only for this purpose, it is
322 * not available to the user as it is for type definition.
325 OperatorCreate(const char *operatorName
,
326 Oid operatorNamespace
,
330 List
*commutatorName
,
337 Relation pg_operator_desc
;
339 char nulls
[Natts_pg_operator
];
340 char replaces
[Natts_pg_operator
];
341 Datum values
[Natts_pg_operator
];
342 Oid operatorObjectId
;
343 bool operatorAlreadyDefined
;
347 bool selfCommutator
= false;
355 if (!validOperatorName(operatorName
))
357 (errcode(ERRCODE_INVALID_NAME
),
358 errmsg("\"%s\" is not a valid operator name",
361 if (!(OidIsValid(leftTypeId
) && OidIsValid(rightTypeId
)))
363 /* If it's not a binary op, these things mustn't be set: */
366 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
367 errmsg("only binary operators can have commutators")));
368 if (OidIsValid(joinId
))
370 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
371 errmsg("only binary operators can have join selectivity")));
374 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
375 errmsg("only binary operators can merge join")));
378 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
379 errmsg("only binary operators can hash")));
382 operResultType
= get_func_rettype(procedureId
);
384 if (operResultType
!= BOOLOID
)
386 /* If it's not a boolean op, these things mustn't be set: */
389 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
390 errmsg("only boolean operators can have negators")));
391 if (OidIsValid(restrictionId
))
393 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
394 errmsg("only boolean operators can have restriction selectivity")));
395 if (OidIsValid(joinId
))
397 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
398 errmsg("only boolean operators can have join selectivity")));
401 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
402 errmsg("only boolean operators can merge join")));
405 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
406 errmsg("only boolean operators can hash")));
409 operatorObjectId
= OperatorGet(operatorName
,
413 &operatorAlreadyDefined
);
415 if (operatorAlreadyDefined
)
417 (errcode(ERRCODE_DUPLICATE_FUNCTION
),
418 errmsg("operator %s already exists",
422 * At this point, if operatorObjectId is not InvalidOid then we are
423 * filling in a previously-created shell. Insist that the user own
426 if (OidIsValid(operatorObjectId
) &&
427 !pg_oper_ownercheck(operatorObjectId
, GetUserId()))
428 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPER
,
432 * Set up the other operators. If they do not currently exist, create
433 * shells in order to get ObjectId's.
438 /* commutator has reversed arg types */
439 commutatorId
= get_other_operator(commutatorName
,
440 rightTypeId
, leftTypeId
,
441 operatorName
, operatorNamespace
,
442 leftTypeId
, rightTypeId
,
445 /* Permission check: must own other operator */
446 if (OidIsValid(commutatorId
) &&
447 !pg_oper_ownercheck(commutatorId
, GetUserId()))
448 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPER
,
449 NameListToString(commutatorName
));
452 * self-linkage to this operator; will fix below. Note that only
453 * self-linkage for commutation makes sense.
455 if (!OidIsValid(commutatorId
))
456 selfCommutator
= true;
459 commutatorId
= InvalidOid
;
463 /* negator has same arg types */
464 negatorId
= get_other_operator(negatorName
,
465 leftTypeId
, rightTypeId
,
466 operatorName
, operatorNamespace
,
467 leftTypeId
, rightTypeId
,
470 /* Permission check: must own other operator */
471 if (OidIsValid(negatorId
) &&
472 !pg_oper_ownercheck(negatorId
, GetUserId()))
473 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPER
,
474 NameListToString(negatorName
));
477 negatorId
= InvalidOid
;
480 * set up values in the operator tuple
483 for (i
= 0; i
< Natts_pg_operator
; ++i
)
485 values
[i
] = (Datum
) NULL
;
491 namestrcpy(&oname
, operatorName
);
492 values
[i
++] = NameGetDatum(&oname
); /* oprname */
493 values
[i
++] = ObjectIdGetDatum(operatorNamespace
); /* oprnamespace */
494 values
[i
++] = ObjectIdGetDatum(GetUserId()); /* oprowner */
495 values
[i
++] = CharGetDatum(leftTypeId
? (rightTypeId
? 'b' : 'r') : 'l'); /* oprkind */
496 values
[i
++] = BoolGetDatum(canMerge
); /* oprcanmerge */
497 values
[i
++] = BoolGetDatum(canHash
); /* oprcanhash */
498 values
[i
++] = ObjectIdGetDatum(leftTypeId
); /* oprleft */
499 values
[i
++] = ObjectIdGetDatum(rightTypeId
); /* oprright */
500 values
[i
++] = ObjectIdGetDatum(operResultType
); /* oprresult */
501 values
[i
++] = ObjectIdGetDatum(commutatorId
); /* oprcom */
502 values
[i
++] = ObjectIdGetDatum(negatorId
); /* oprnegate */
503 values
[i
++] = ObjectIdGetDatum(procedureId
); /* oprcode */
504 values
[i
++] = ObjectIdGetDatum(restrictionId
); /* oprrest */
505 values
[i
++] = ObjectIdGetDatum(joinId
); /* oprjoin */
507 pg_operator_desc
= heap_open(OperatorRelationId
, RowExclusiveLock
);
510 * If we are replacing an operator shell, update; else insert
512 if (operatorObjectId
)
514 tup
= SearchSysCacheCopy(OPEROID
,
515 ObjectIdGetDatum(operatorObjectId
),
517 if (!HeapTupleIsValid(tup
))
518 elog(ERROR
, "cache lookup failed for operator %u",
521 tup
= heap_modifytuple(tup
,
522 RelationGetDescr(pg_operator_desc
),
527 simple_heap_update(pg_operator_desc
, &tup
->t_self
, tup
);
531 tupDesc
= pg_operator_desc
->rd_att
;
532 tup
= heap_formtuple(tupDesc
, values
, nulls
);
534 operatorObjectId
= simple_heap_insert(pg_operator_desc
, tup
);
537 /* Must update the indexes in either case */
538 CatalogUpdateIndexes(pg_operator_desc
, tup
);
540 /* Add dependencies for the entry */
541 makeOperatorDependencies(tup
);
543 heap_close(pg_operator_desc
, RowExclusiveLock
);
546 * If a commutator and/or negator link is provided, update the other
547 * operator(s) to point at this one, if they don't already have a link.
548 * This supports an alternative style of operator definition wherein the
549 * user first defines one operator without giving negator or commutator,
550 * then defines the other operator of the pair with the proper commutator
551 * or negator attribute. That style doesn't require creation of a shell,
552 * and it's the only style that worked right before Postgres version 6.5.
553 * This code also takes care of the situation where the new operator is
554 * its own commutator.
557 commutatorId
= operatorObjectId
;
559 if (OidIsValid(commutatorId
) || OidIsValid(negatorId
))
560 OperatorUpd(operatorObjectId
, commutatorId
, negatorId
);
564 * Try to lookup another operator (commutator, etc)
566 * If not found, check to see if it is exactly the operator we are trying
567 * to define; if so, return InvalidOid. (Note that this case is only
568 * sensible for a commutator, so we error out otherwise.) If it is not
569 * the same operator, create a shell operator.
572 get_other_operator(List
*otherOp
, Oid otherLeftTypeId
, Oid otherRightTypeId
,
573 const char *operatorName
, Oid operatorNamespace
,
574 Oid leftTypeId
, Oid rightTypeId
, bool isCommutator
)
582 other_oid
= OperatorLookup(otherOp
,
587 if (OidIsValid(other_oid
))
589 /* other op already in catalogs */
593 otherNamespace
= QualifiedNameGetCreationNamespace(otherOp
,
596 if (strcmp(otherName
, operatorName
) == 0 &&
597 otherNamespace
== operatorNamespace
&&
598 otherLeftTypeId
== leftTypeId
&&
599 otherRightTypeId
== rightTypeId
)
602 * self-linkage to this operator; caller will fix later. Note that
603 * only self-linkage for commutation makes sense.
607 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION
),
608 errmsg("operator cannot be its own negator or sort operator")));
612 /* not in catalogs, different from operator, so make shell */
614 aclresult
= pg_namespace_aclcheck(otherNamespace
, GetUserId(),
616 if (aclresult
!= ACLCHECK_OK
)
617 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
618 get_namespace_name(otherNamespace
));
620 other_oid
= OperatorShellMake(otherName
,
630 * For a given operator, look up its negator and commutator operators.
631 * If they are defined, but their negator and commutator fields
632 * (respectively) are empty, then use the new operator for neg or comm.
633 * This solves a problem for users who need to insert two new operators
634 * which are the negator or commutator of each other.
637 OperatorUpd(Oid baseId
, Oid commId
, Oid negId
)
640 Relation pg_operator_desc
;
642 char nulls
[Natts_pg_operator
];
643 char replaces
[Natts_pg_operator
];
644 Datum values
[Natts_pg_operator
];
646 for (i
= 0; i
< Natts_pg_operator
; ++i
)
648 values
[i
] = (Datum
) 0;
654 * check and update the commutator & negator, if necessary
656 * We need a CommandCounterIncrement here in case of a self-commutator
657 * operator: we'll need to update the tuple that we just inserted.
659 CommandCounterIncrement();
661 pg_operator_desc
= heap_open(OperatorRelationId
, RowExclusiveLock
);
663 tup
= SearchSysCacheCopy(OPEROID
,
664 ObjectIdGetDatum(commId
),
668 * if the commutator and negator are the same operator, do one update. XXX
669 * this is probably useless code --- I doubt it ever makes sense for
670 * commutator and negator to be the same thing...
674 if (HeapTupleIsValid(tup
))
676 Form_pg_operator t
= (Form_pg_operator
) GETSTRUCT(tup
);
678 if (!OidIsValid(t
->oprcom
) || !OidIsValid(t
->oprnegate
))
680 if (!OidIsValid(t
->oprnegate
))
682 values
[Anum_pg_operator_oprnegate
- 1] = ObjectIdGetDatum(baseId
);
683 replaces
[Anum_pg_operator_oprnegate
- 1] = 'r';
686 if (!OidIsValid(t
->oprcom
))
688 values
[Anum_pg_operator_oprcom
- 1] = ObjectIdGetDatum(baseId
);
689 replaces
[Anum_pg_operator_oprcom
- 1] = 'r';
692 tup
= heap_modifytuple(tup
,
693 RelationGetDescr(pg_operator_desc
),
698 simple_heap_update(pg_operator_desc
, &tup
->t_self
, tup
);
700 CatalogUpdateIndexes(pg_operator_desc
, tup
);
704 heap_close(pg_operator_desc
, RowExclusiveLock
);
709 /* if commutator and negator are different, do two updates */
711 if (HeapTupleIsValid(tup
) &&
712 !(OidIsValid(((Form_pg_operator
) GETSTRUCT(tup
))->oprcom
)))
714 values
[Anum_pg_operator_oprcom
- 1] = ObjectIdGetDatum(baseId
);
715 replaces
[Anum_pg_operator_oprcom
- 1] = 'r';
717 tup
= heap_modifytuple(tup
,
718 RelationGetDescr(pg_operator_desc
),
723 simple_heap_update(pg_operator_desc
, &tup
->t_self
, tup
);
725 CatalogUpdateIndexes(pg_operator_desc
, tup
);
727 values
[Anum_pg_operator_oprcom
- 1] = (Datum
) NULL
;
728 replaces
[Anum_pg_operator_oprcom
- 1] = ' ';
731 /* check and update the negator, if necessary */
733 tup
= SearchSysCacheCopy(OPEROID
,
734 ObjectIdGetDatum(negId
),
737 if (HeapTupleIsValid(tup
) &&
738 !(OidIsValid(((Form_pg_operator
) GETSTRUCT(tup
))->oprnegate
)))
740 values
[Anum_pg_operator_oprnegate
- 1] = ObjectIdGetDatum(baseId
);
741 replaces
[Anum_pg_operator_oprnegate
- 1] = 'r';
743 tup
= heap_modifytuple(tup
,
744 RelationGetDescr(pg_operator_desc
),
749 simple_heap_update(pg_operator_desc
, &tup
->t_self
, tup
);
751 CatalogUpdateIndexes(pg_operator_desc
, tup
);
754 heap_close(pg_operator_desc
, RowExclusiveLock
);
758 * Create dependencies for a new operator (either a freshly inserted
759 * complete operator, a new shell operator, or a just-updated shell).
761 * NB: the OidIsValid tests in this routine are necessary, in case
762 * the given operator is a shell.
765 makeOperatorDependencies(HeapTuple tuple
)
767 Form_pg_operator oper
= (Form_pg_operator
) GETSTRUCT(tuple
);
768 ObjectAddress myself
,
771 myself
.classId
= OperatorRelationId
;
772 myself
.objectId
= HeapTupleGetOid(tuple
);
773 myself
.objectSubId
= 0;
775 /* In case we are updating a shell, delete any existing entries */
776 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
);
777 deleteSharedDependencyRecordsFor(myself
.classId
, myself
.objectId
);
779 /* Dependency on namespace */
780 if (OidIsValid(oper
->oprnamespace
))
782 referenced
.classId
= NamespaceRelationId
;
783 referenced
.objectId
= oper
->oprnamespace
;
784 referenced
.objectSubId
= 0;
785 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
788 /* Dependency on left type */
789 if (OidIsValid(oper
->oprleft
))
791 referenced
.classId
= TypeRelationId
;
792 referenced
.objectId
= oper
->oprleft
;
793 referenced
.objectSubId
= 0;
794 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
797 /* Dependency on right type */
798 if (OidIsValid(oper
->oprright
))
800 referenced
.classId
= TypeRelationId
;
801 referenced
.objectId
= oper
->oprright
;
802 referenced
.objectSubId
= 0;
803 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
806 /* Dependency on result type */
807 if (OidIsValid(oper
->oprresult
))
809 referenced
.classId
= TypeRelationId
;
810 referenced
.objectId
= oper
->oprresult
;
811 referenced
.objectSubId
= 0;
812 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
816 * NOTE: we do not consider the operator to depend on the associated
817 * operators oprcom and oprnegate. We would not want to delete this
818 * operator if those go away, but only reset the link fields; which is not
819 * a function that the dependency code can presently handle. (Something
820 * could perhaps be done with objectSubId though.) For now, it's okay to
821 * let those links dangle if a referenced operator is removed.
824 /* Dependency on implementation function */
825 if (OidIsValid(oper
->oprcode
))
827 referenced
.classId
= ProcedureRelationId
;
828 referenced
.objectId
= oper
->oprcode
;
829 referenced
.objectSubId
= 0;
830 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
833 /* Dependency on restriction selectivity function */
834 if (OidIsValid(oper
->oprrest
))
836 referenced
.classId
= ProcedureRelationId
;
837 referenced
.objectId
= oper
->oprrest
;
838 referenced
.objectSubId
= 0;
839 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
842 /* Dependency on join selectivity function */
843 if (OidIsValid(oper
->oprjoin
))
845 referenced
.classId
= ProcedureRelationId
;
846 referenced
.objectId
= oper
->oprjoin
;
847 referenced
.objectSubId
= 0;
848 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
851 /* Dependency on owner */
852 recordDependencyOnOwner(OperatorRelationId
, HeapTupleGetOid(tuple
),