/*-------------------------------------------------------------------------
 *
 * pg_operator.c
 *	  routines to support manipulation of the pg_operator relation
 *
 * Portions Copyright (c) 1996-2025, 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	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);


/*
 * 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)
 */
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)));

	operResultType = get_func_rettype(procedureId);

	OperatorValidateParams(leftTypeId,
						   rightTypeId,
						   operResultType,
						   commutatorName != NIL,
						   negatorName != NIL,
						   OidIsValid(restrictionId),
						   OidIsValid(joinId),
						   canMerge,
						   canHash);

	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) &&
		!object_ownercheck(OperatorRelationId, 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);

		/* Permission check: must own other operator */
		if (OidIsValid(commutatorId) &&
			!object_ownercheck(OperatorRelationId, commutatorId, GetUserId()))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
						   NameListToString(commutatorName));

		/*
		 * If self-linkage to the new operator is requested, we'll fix it
		 * below.  (In case of self-linkage to an existing shell operator, we
		 * need do nothing special.)
		 */
		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);

		/* Permission check: must own other operator */
		if (OidIsValid(negatorId) &&
			!object_ownercheck(OperatorRelationId, negatorId, GetUserId()))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
						   NameListToString(negatorName));

		/*
		 * Prevent self negation, as it doesn't make sense.  It's self
		 * negation if result is InvalidOid (negator would be the same
		 * operator but it doesn't exist yet) or operatorObjectId (we are
		 * replacing a shell that would need to be its own negator).
		 */
		if (!OidIsValid(negatorId) || negatorId == operatorObjectId)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("operator cannot be its own negator")));
	}
	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);

	/*
	 * 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);

	/* Post creation hook for new operator */
	InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);

	table_close(pg_operator_desc, RowExclusiveLock);

	return address;
}

/*
 * OperatorValidateParams
 *
 * Check that an operator with argument types leftTypeId and rightTypeId,
 * returning operResultType, can have the attributes that are set to true.
 * Raise an error for any disallowed attribute.
 *
 * Note: in ALTER OPERATOR, we only bother to pass "true" for attributes
 * the command is trying to set, not those that may already be set.
 * This is OK as long as the attribute checks are independent.
 */
void
OperatorValidateParams(Oid leftTypeId,
					   Oid rightTypeId,
					   Oid operResultType,
					   bool hasCommutator,
					   bool hasNegator,
					   bool hasRestrictionSelectivity,
					   bool hasJoinSelectivity,
					   bool canMerge,
					   bool canHash)
{
	if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
	{
		/* If it's not a binary op, these things mustn't be set: */
		if (hasCommutator)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can have commutators")));
		if (hasJoinSelectivity)
			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")));
	}

	if (operResultType != BOOLOID)
	{
		/* If it's not a boolean op, these things mustn't be set: */
		if (hasNegator)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have negators")));
		if (hasRestrictionSelectivity)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have restriction selectivity")));
		if (hasJoinSelectivity)
			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")));
	}
}

/*
 * Try to lookup another operator (commutator, etc); return its OID
 *
 * If not found, check to see if it would be the same operator we are trying
 * to define; if so, return InvalidOid.  (Caller must decide whether
 * that is sensible.)  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)
{
	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 new operator; caller must handle this */
		return InvalidOid;
	}

	/* not in catalogs, different from operator, so make shell */

	aclresult = object_aclcheck(NamespaceRelationId, 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 be InvalidOid.
 *
 *	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;

		/*
		 * We can skip doing anything if the commutator's oprcom field is
		 * already what we want.  While that's not expected in the isDelete
		 * case, it's perfectly possible when filling in a shell operator.
		 */
		if (isDelete && OidIsValid(t->oprcom))
		{
			t->oprcom = InvalidOid;
			update_commutator = true;
		}
		else if (!isDelete && t->oprcom != baseId)
		{
			/*
			 * If commutator's oprcom field is already set to point to some
			 * third operator, it's an error.  Changing its link would be
			 * unsafe, and letting the inconsistency stand would not be good
			 * either.  This might be indicative of catalog corruption, so
			 * don't assume t->oprcom is necessarily a valid operator.
			 */
			if (OidIsValid(t->oprcom))
			{
				char	   *thirdop = get_opname(t->oprcom);

				if (thirdop != NULL)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("commutator operator %s is already the commutator of operator %s",
									NameStr(t->oprname), thirdop)));
				else
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("commutator operator %s is already the commutator of operator %u",
									NameStr(t->oprname), 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;

		/*
		 * We can skip doing anything if the negator's oprnegate field is
		 * already what we want.  While that's not expected in the isDelete
		 * case, it's perfectly possible when filling in a shell operator.
		 */
		if (isDelete && OidIsValid(t->oprnegate))
		{
			t->oprnegate = InvalidOid;
			update_negator = true;
		}
		else if (!isDelete && t->oprnegate != baseId)
		{
			/*
			 * If negator's oprnegate field is already set to point to some
			 * third operator, it's an error.  Changing its link would be
			 * unsafe, and letting the inconsistency stand would not be good
			 * either.  This might be indicative of catalog corruption, so
			 * don't assume t->oprnegate is necessarily a valid operator.
			 */
			if (OidIsValid(t->oprnegate))
			{
				char	   *thirdop = get_opname(t->oprnegate);

				if (thirdop != NULL)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("negator operator %s is already the negator of operator %s",
									NameStr(t->oprname), thirdop)));
				else
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("negator operator %s is already the negator of operator %u",
									NameStr(t->oprname), 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 do not want to delete this operator
	 * if those go away, but only reset the link fields; which is not a
	 * function that the dependency logic can handle.  (It's taken care of
	 * manually within RemoveOperatorById, instead.)
	 */

	/* 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;
}
