summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/pg_operator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/pg_operator.c')
-rw-r--r--src/backend/catalog/pg_operator.c870
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;
+}