diff options
Diffstat (limited to 'src/backend/catalog/pg_operator.c')
-rw-r--r-- | src/backend/catalog/pg_operator.c | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c new file mode 100644 index 0000000..3947ad8 --- /dev/null +++ b/src/backend/catalog/pg_operator.c @@ -0,0 +1,870 @@ +/*------------------------------------------------------------------------- + * + * pg_operator.c + * routines to support manipulation of the pg_operator relation + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_operator.c + * + * NOTES + * these routines moved here from commands/define.c and somewhat cleaned up. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/table.h" +#include "access/xact.h" +#include "catalog/catalog.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "parser/parse_oper.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" + + +static Oid OperatorGet(const char *operatorName, + Oid operatorNamespace, + Oid leftObjectId, + Oid rightObjectId, + bool *defined); + +static Oid OperatorLookup(List *operatorName, + Oid leftObjectId, + Oid rightObjectId, + bool *defined); + +static Oid OperatorShellMake(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId); + +static Oid get_other_operator(List *otherOp, + Oid otherLeftTypeId, Oid otherRightTypeId, + const char *operatorName, Oid operatorNamespace, + Oid leftTypeId, Oid rightTypeId, + bool isCommutator); + + +/* + * Check whether a proposed operator name is legal + * + * This had better match the behavior of parser/scan.l! + * + * We need this because the parser is not smart enough to check that + * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses + * are operator names rather than some other lexical entity. + */ +static bool +validOperatorName(const char *name) +{ + size_t len = strlen(name); + + /* Can't be empty or too long */ + if (len == 0 || len >= NAMEDATALEN) + return false; + + /* Can't contain any invalid characters */ + /* Test string here should match op_chars in scan.l */ + if (strspn(name, "~!@#^&|`?+-*/%<>=") != len) + return false; + + /* Can't contain slash-star or dash-dash (comment starts) */ + if (strstr(name, "/*") || strstr(name, "--")) + return false; + + /* + * For SQL standard compatibility, '+' and '-' cannot be the last char of + * a multi-char operator unless the operator contains chars that are not + * in SQL operators. The idea is to lex '=-' as two operators, but not to + * forbid operator names like '?-' that could not be sequences of standard + * SQL operators. + */ + if (len > 1 && + (name[len - 1] == '+' || + name[len - 1] == '-')) + { + int ic; + + for (ic = len - 2; ic >= 0; ic--) + { + if (strchr("~!@#^&|`?%", name[ic])) + break; + } + if (ic < 0) + return false; /* nope, not valid */ + } + + /* != isn't valid either, because parser will convert it to <> */ + if (strcmp(name, "!=") == 0) + return false; + + return true; +} + + +/* + * OperatorGet + * + * finds an operator given an exact specification (name, namespace, + * left and right type IDs). + * + * *defined is set true if defined (not a shell) + */ +static Oid +OperatorGet(const char *operatorName, + Oid operatorNamespace, + Oid leftObjectId, + Oid rightObjectId, + bool *defined) +{ + HeapTuple tup; + Oid operatorObjectId; + + tup = SearchSysCache4(OPERNAMENSP, + PointerGetDatum(operatorName), + ObjectIdGetDatum(leftObjectId), + ObjectIdGetDatum(rightObjectId), + ObjectIdGetDatum(operatorNamespace)); + if (HeapTupleIsValid(tup)) + { + Form_pg_operator oprform = (Form_pg_operator) GETSTRUCT(tup); + + operatorObjectId = oprform->oid; + *defined = RegProcedureIsValid(oprform->oprcode); + ReleaseSysCache(tup); + } + else + { + operatorObjectId = InvalidOid; + *defined = false; + } + + return operatorObjectId; +} + +/* + * OperatorLookup + * + * looks up an operator given a possibly-qualified name and + * left and right type IDs. + * + * *defined is set true if defined (not a shell) + */ +static Oid +OperatorLookup(List *operatorName, + Oid leftObjectId, + Oid rightObjectId, + bool *defined) +{ + Oid operatorObjectId; + RegProcedure oprcode; + + operatorObjectId = LookupOperName(NULL, operatorName, + leftObjectId, rightObjectId, + true, -1); + if (!OidIsValid(operatorObjectId)) + { + *defined = false; + return InvalidOid; + } + + oprcode = get_opcode(operatorObjectId); + *defined = RegProcedureIsValid(oprcode); + + return operatorObjectId; +} + + +/* + * OperatorShellMake + * Make a "shell" entry for a not-yet-existing operator. + */ +static Oid +OperatorShellMake(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId) +{ + Relation pg_operator_desc; + Oid operatorObjectId; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; + bool nulls[Natts_pg_operator]; + NameData oname; + TupleDesc tupDesc; + + /* + * validate operator name + */ + if (!validOperatorName(operatorName)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("\"%s\" is not a valid operator name", + operatorName))); + + /* + * open pg_operator + */ + pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock); + tupDesc = pg_operator_desc->rd_att; + + /* + * initialize our *nulls and *values arrays + */ + for (i = 0; i < Natts_pg_operator; ++i) + { + nulls[i] = false; + values[i] = (Datum) NULL; /* redundant, but safe */ + } + + /* + * initialize values[] with the operator name and input data types. Note + * that oprcode is set to InvalidOid, indicating it's a shell. + */ + operatorObjectId = GetNewOidWithIndex(pg_operator_desc, OperatorOidIndexId, + Anum_pg_operator_oid); + values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId); + namestrcpy(&oname, operatorName); + values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); + values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); + values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l'); + values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false); + values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false); + values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); + values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); + values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); + + /* + * create a new operator tuple + */ + tup = heap_form_tuple(tupDesc, values, nulls); + + /* + * insert our "shell" operator tuple + */ + CatalogTupleInsert(pg_operator_desc, tup); + + /* Add dependencies for the entry */ + makeOperatorDependencies(tup, true, false); + + heap_freetuple(tup); + + /* Post creation hook for new shell operator */ + InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); + + /* + * Make sure the tuple is visible for subsequent lookups/updates. + */ + CommandCounterIncrement(); + + /* + * close the operator relation and return the oid. + */ + table_close(pg_operator_desc, RowExclusiveLock); + + return operatorObjectId; +} + +/* + * OperatorCreate + * + * "X" indicates an optional argument (i.e. one that can be NULL or 0) + * operatorName name for new operator + * operatorNamespace namespace for new operator + * leftTypeId X left type ID + * rightTypeId X right type ID + * procedureId procedure ID for operator + * commutatorName X commutator operator + * negatorName X negator operator + * restrictionId X restriction selectivity procedure ID + * joinId X join selectivity procedure ID + * canMerge merge join can be used with this operator + * canHash hash join can be used with this operator + * + * The caller should have validated properties and permissions for the + * objects passed as OID references. We must handle the commutator and + * negator operator references specially, however, since those need not + * exist beforehand. + * + * This routine gets complicated because it allows the user to + * specify operators that do not exist. For example, if operator + * "op" is being defined, the negator operator "negop" and the + * commutator "commop" can also be defined without specifying + * any information other than their names. Since in order to + * add "op" to the PG_OPERATOR catalog, all the Oid's for these + * operators must be placed in the fields of "op", a forward + * declaration is done on the commutator and negator operators. + * This is called creating a shell, and its main effect is to + * create a tuple in the PG_OPERATOR catalog with minimal + * information about the operator (just its name and types). + * Forward declaration is used only for this purpose, it is + * not available to the user as it is for type definition. + */ +ObjectAddress +OperatorCreate(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId, + Oid procedureId, + List *commutatorName, + List *negatorName, + Oid restrictionId, + Oid joinId, + bool canMerge, + bool canHash) +{ + Relation pg_operator_desc; + HeapTuple tup; + bool isUpdate; + bool nulls[Natts_pg_operator]; + bool replaces[Natts_pg_operator]; + Datum values[Natts_pg_operator]; + Oid operatorObjectId; + bool operatorAlreadyDefined; + Oid operResultType; + Oid commutatorId, + negatorId; + bool selfCommutator = false; + NameData oname; + int i; + ObjectAddress address; + + /* + * Sanity checks + */ + if (!validOperatorName(operatorName)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("\"%s\" is not a valid operator name", + operatorName))); + + if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) + { + /* If it's not a binary op, these things mustn't be set: */ + if (commutatorName) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can have commutators"))); + if (OidIsValid(joinId)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can have join selectivity"))); + if (canMerge) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can merge join"))); + if (canHash) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can hash"))); + } + + operResultType = get_func_rettype(procedureId); + + if (operResultType != BOOLOID) + { + /* If it's not a boolean op, these things mustn't be set: */ + if (negatorName) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have negators"))); + if (OidIsValid(restrictionId)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have restriction selectivity"))); + if (OidIsValid(joinId)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have join selectivity"))); + if (canMerge) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can merge join"))); + if (canHash) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can hash"))); + } + + operatorObjectId = OperatorGet(operatorName, + operatorNamespace, + leftTypeId, + rightTypeId, + &operatorAlreadyDefined); + + if (operatorAlreadyDefined) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("operator %s already exists", + operatorName))); + + /* + * At this point, if operatorObjectId is not InvalidOid then we are + * filling in a previously-created shell. Insist that the user own any + * such shell. + */ + if (OidIsValid(operatorObjectId) && + !pg_oper_ownercheck(operatorObjectId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR, + operatorName); + + /* + * Set up the other operators. If they do not currently exist, create + * shells in order to get ObjectId's. + */ + + if (commutatorName) + { + /* commutator has reversed arg types */ + commutatorId = get_other_operator(commutatorName, + rightTypeId, leftTypeId, + operatorName, operatorNamespace, + leftTypeId, rightTypeId, + true); + + /* Permission check: must own other operator */ + if (OidIsValid(commutatorId) && + !pg_oper_ownercheck(commutatorId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR, + NameListToString(commutatorName)); + + /* + * self-linkage to this operator; will fix below. Note that only + * self-linkage for commutation makes sense. + */ + if (!OidIsValid(commutatorId)) + selfCommutator = true; + } + else + commutatorId = InvalidOid; + + if (negatorName) + { + /* negator has same arg types */ + negatorId = get_other_operator(negatorName, + leftTypeId, rightTypeId, + operatorName, operatorNamespace, + leftTypeId, rightTypeId, + false); + + /* Permission check: must own other operator */ + if (OidIsValid(negatorId) && + !pg_oper_ownercheck(negatorId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR, + NameListToString(negatorName)); + } + else + negatorId = InvalidOid; + + /* + * set up values in the operator tuple + */ + + for (i = 0; i < Natts_pg_operator; ++i) + { + values[i] = (Datum) NULL; + replaces[i] = true; + nulls[i] = false; + } + + namestrcpy(&oname, operatorName); + values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); + values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); + values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? 'b' : 'l'); + values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); + values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); + values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); + values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); + values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType); + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId); + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId); + values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId); + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId); + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId); + + pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock); + + /* + * If we are replacing an operator shell, update; else insert + */ + if (operatorObjectId) + { + isUpdate = true; + + tup = SearchSysCacheCopy1(OPEROID, + ObjectIdGetDatum(operatorObjectId)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for operator %u", + operatorObjectId); + + replaces[Anum_pg_operator_oid - 1] = false; + tup = heap_modify_tuple(tup, + RelationGetDescr(pg_operator_desc), + values, + nulls, + replaces); + + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); + } + else + { + isUpdate = false; + + operatorObjectId = GetNewOidWithIndex(pg_operator_desc, + OperatorOidIndexId, + Anum_pg_operator_oid); + values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(operatorObjectId); + + tup = heap_form_tuple(RelationGetDescr(pg_operator_desc), + values, nulls); + + CatalogTupleInsert(pg_operator_desc, tup); + } + + /* Add dependencies for the entry */ + address = makeOperatorDependencies(tup, true, isUpdate); + + /* Post creation hook for new operator */ + InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); + + table_close(pg_operator_desc, RowExclusiveLock); + + /* + * If a commutator and/or negator link is provided, update the other + * operator(s) to point at this one, if they don't already have a link. + * This supports an alternative style of operator definition wherein the + * user first defines one operator without giving negator or commutator, + * then defines the other operator of the pair with the proper commutator + * or negator attribute. That style doesn't require creation of a shell, + * and it's the only style that worked right before Postgres version 6.5. + * This code also takes care of the situation where the new operator is + * its own commutator. + */ + if (selfCommutator) + commutatorId = operatorObjectId; + + if (OidIsValid(commutatorId) || OidIsValid(negatorId)) + OperatorUpd(operatorObjectId, commutatorId, negatorId, false); + + return address; +} + +/* + * Try to lookup another operator (commutator, etc) + * + * If not found, check to see if it is exactly the operator we are trying + * to define; if so, return InvalidOid. (Note that this case is only + * sensible for a commutator, so we error out otherwise.) If it is not + * the same operator, create a shell operator. + */ +static Oid +get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, + const char *operatorName, Oid operatorNamespace, + Oid leftTypeId, Oid rightTypeId, bool isCommutator) +{ + Oid other_oid; + bool otherDefined; + char *otherName; + Oid otherNamespace; + AclResult aclresult; + + other_oid = OperatorLookup(otherOp, + otherLeftTypeId, + otherRightTypeId, + &otherDefined); + + if (OidIsValid(other_oid)) + { + /* other op already in catalogs */ + return other_oid; + } + + otherNamespace = QualifiedNameGetCreationNamespace(otherOp, + &otherName); + + if (strcmp(otherName, operatorName) == 0 && + otherNamespace == operatorNamespace && + otherLeftTypeId == leftTypeId && + otherRightTypeId == rightTypeId) + { + /* + * self-linkage to this operator; caller will fix later. Note that + * only self-linkage for commutation makes sense. + */ + if (!isCommutator) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator cannot be its own negator or sort operator"))); + return InvalidOid; + } + + /* not in catalogs, different from operator, so make shell */ + + aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_SCHEMA, + get_namespace_name(otherNamespace)); + + other_oid = OperatorShellMake(otherName, + otherNamespace, + otherLeftTypeId, + otherRightTypeId); + return other_oid; +} + +/* + * OperatorUpd + * + * For a given operator, look up its negator and commutator operators. + * When isDelete is false, update their negator and commutator fields to + * point back to the given operator; when isDelete is true, update those + * fields to no longer point back to the given operator. + * + * The !isDelete case solves a problem for users who need to insert two new + * operators that are the negator or commutator of each other, while the + * isDelete case is needed so as not to leave dangling OID links behind + * after dropping an operator. + */ +void +OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) +{ + Relation pg_operator_desc; + HeapTuple tup; + + /* + * If we're making an operator into its own commutator, then we need a + * command-counter increment here, since we've just inserted the tuple + * we're about to update. But when we're dropping an operator, we can + * skip this because we're at the beginning of the command. + */ + if (!isDelete) + CommandCounterIncrement(); + + /* Open the relation. */ + pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock); + + /* Get a writable copy of the commutator's tuple. */ + if (OidIsValid(commId)) + tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId)); + else + tup = NULL; + + /* Update the commutator's tuple if need be. */ + if (HeapTupleIsValid(tup)) + { + Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); + bool update_commutator = false; + + /* + * Out of due caution, we only change the commutator's oprcom field if + * it has the exact value we expected: InvalidOid when creating an + * operator, or baseId when dropping one. + */ + if (isDelete && t->oprcom == baseId) + { + t->oprcom = InvalidOid; + update_commutator = true; + } + else if (!isDelete && !OidIsValid(t->oprcom)) + { + t->oprcom = baseId; + update_commutator = true; + } + + /* If any columns were found to need modification, update tuple. */ + if (update_commutator) + { + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); + + /* + * Do CCI to make the updated tuple visible. We must do this in + * case the commutator is also the negator. (Which would be a + * logic error on the operator definer's part, but that's not a + * good reason to fail here.) We would need a CCI anyway in the + * deletion case for a self-commutator with no negator. + */ + CommandCounterIncrement(); + } + } + + /* + * Similarly find and update the negator, if any. + */ + if (OidIsValid(negId)) + tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId)); + else + tup = NULL; + + if (HeapTupleIsValid(tup)) + { + Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); + bool update_negator = false; + + /* + * Out of due caution, we only change the negator's oprnegate field if + * it has the exact value we expected: InvalidOid when creating an + * operator, or baseId when dropping one. + */ + if (isDelete && t->oprnegate == baseId) + { + t->oprnegate = InvalidOid; + update_negator = true; + } + else if (!isDelete && !OidIsValid(t->oprnegate)) + { + t->oprnegate = baseId; + update_negator = true; + } + + /* If any columns were found to need modification, update tuple. */ + if (update_negator) + { + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); + + /* + * In the deletion case, do CCI to make the updated tuple visible. + * We must do this in case the operator is its own negator. (Which + * would be a logic error on the operator definer's part, but + * that's not a good reason to fail here.) + */ + if (isDelete) + CommandCounterIncrement(); + } + } + + /* Close relation and release catalog lock. */ + table_close(pg_operator_desc, RowExclusiveLock); +} + +/* + * Create dependencies for an operator (either a freshly inserted + * complete operator, a new shell operator, a just-updated shell, + * or an operator that's being modified by ALTER OPERATOR). + * + * makeExtensionDep should be true when making a new operator or + * replacing a shell, false for ALTER OPERATOR. Passing false + * will prevent any change in the operator's extension membership. + * + * NB: the OidIsValid tests in this routine are necessary, in case + * the given operator is a shell. + */ +ObjectAddress +makeOperatorDependencies(HeapTuple tuple, + bool makeExtensionDep, + bool isUpdate) +{ + Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple); + ObjectAddress myself, + referenced; + ObjectAddresses *addrs; + + ObjectAddressSet(myself, OperatorRelationId, oper->oid); + + /* + * If we are updating the operator, delete any existing entries, except + * for extension membership which should remain the same. + */ + if (isUpdate) + { + deleteDependencyRecordsFor(myself.classId, myself.objectId, true); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); + } + + addrs = new_object_addresses(); + + /* Dependency on namespace */ + if (OidIsValid(oper->oprnamespace)) + { + ObjectAddressSet(referenced, NamespaceRelationId, oper->oprnamespace); + add_exact_object_address(&referenced, addrs); + } + + /* Dependency on left type */ + if (OidIsValid(oper->oprleft)) + { + ObjectAddressSet(referenced, TypeRelationId, oper->oprleft); + add_exact_object_address(&referenced, addrs); + } + + /* Dependency on right type */ + if (OidIsValid(oper->oprright)) + { + ObjectAddressSet(referenced, TypeRelationId, oper->oprright); + add_exact_object_address(&referenced, addrs); + } + + /* Dependency on result type */ + if (OidIsValid(oper->oprresult)) + { + ObjectAddressSet(referenced, TypeRelationId, oper->oprresult); + add_exact_object_address(&referenced, addrs); + } + + /* + * NOTE: we do not consider the operator to depend on the associated + * operators oprcom and oprnegate. We would not want to delete this + * operator if those go away, but only reset the link fields; which is not + * a function that the dependency code can presently handle. (Something + * could perhaps be done with objectSubId though.) For now, it's okay to + * let those links dangle if a referenced operator is removed. + */ + + /* Dependency on implementation function */ + if (OidIsValid(oper->oprcode)) + { + ObjectAddressSet(referenced, ProcedureRelationId, oper->oprcode); + add_exact_object_address(&referenced, addrs); + } + + /* Dependency on restriction selectivity function */ + if (OidIsValid(oper->oprrest)) + { + ObjectAddressSet(referenced, ProcedureRelationId, oper->oprrest); + add_exact_object_address(&referenced, addrs); + } + + /* Dependency on join selectivity function */ + if (OidIsValid(oper->oprjoin)) + { + ObjectAddressSet(referenced, ProcedureRelationId, oper->oprjoin); + add_exact_object_address(&referenced, addrs); + } + + record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); + free_object_addresses(addrs); + + /* Dependency on owner */ + recordDependencyOnOwner(OperatorRelationId, oper->oid, + oper->oprowner); + + /* Dependency on extension */ + if (makeExtensionDep) + recordDependencyOnCurrentExtension(&myself, isUpdate); + + return myself; +} |