Hello Hackers.

I reworked the patch. Now, the syntax is similar to CREATE OPERATOR.
And as I wrote earlier problems with the cache I have not found. If someone 
can suggest how it could be verified that would be happy.

New syntax example:
ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = =);
ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = =);
ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = example_func);
ALTER OPERATOR === (boolean, boolean) SET (JOIN = example_func);

ALTER OPERATOR === (boolean, boolean) SET (
        COMMUTATOR = NULL,
        NEGATOR = NULL,
        RESTRICT = NULL,
        JOIN = NULL
);


Thanks!

-- 
Uriy Zhuravlev
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 072f530..83e020b 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -28,8 +28,10 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "miscadmin.h"
 #include "parser/parse_oper.h"
+#include "parser/parse_func.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -53,7 +55,7 @@ static Oid OperatorShellMake(const char *operatorName,
 				  Oid leftTypeId,
 				  Oid rightTypeId);
 
-static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
+static void ShellOperatorUpd(Oid baseId, Oid commId, Oid negId);
 
 static Oid get_other_operator(List *otherOp,
 				   Oid otherLeftTypeId, Oid otherRightTypeId,
@@ -563,7 +565,7 @@ OperatorCreate(const char *operatorName,
 		commutatorId = operatorObjectId;
 
 	if (OidIsValid(commutatorId) || OidIsValid(negatorId))
-		OperatorUpd(operatorObjectId, commutatorId, negatorId);
+		ShellOperatorUpd(operatorObjectId, commutatorId, negatorId);
 
 	return address;
 }
@@ -633,7 +635,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
 }
 
 /*
- * OperatorUpd
+ * ShellOperatorUpd
  *
  *	For a given operator, look up its negator and commutator operators.
  *	If they are defined, but their negator and commutator fields
@@ -642,7 +644,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
  *	which are the negator or commutator of each other.
  */
 static void
-OperatorUpd(Oid baseId, Oid commId, Oid negId)
+ShellOperatorUpd(Oid baseId, Oid commId, Oid negId)
 {
 	int			i;
 	Relation	pg_operator_desc;
@@ -864,3 +866,187 @@ makeOperatorDependencies(HeapTuple tuple)
 
 	return myself;
 }
+
+/*
+ * Operator update aka ALTER OPERATOR for COMMUTATOR, NEGATOR, RESTRICT, JOIN
+ */
+void OperatorUpd(Oid classId,
+				 Oid baseId,
+				 List *operator_params)
+{
+	int			i;
+	ListCell	*pl;
+	Relation	catalog;
+	HeapTuple	tup;
+	Oid 		operator_param_id = 0;
+	Form_pg_operator DstOperatorData;
+	bool		otherDefined;
+	bool		nulls[Natts_pg_operator];
+	bool		replaces[Natts_pg_operator];
+	Datum		values[Natts_pg_operator];
+
+	for (i = 0; i < Natts_pg_operator; ++i)
+	{
+		values[i] = (Datum) 0;
+		replaces[i] = false;
+		nulls[i] = false;
+	}
+
+	catalog = heap_open(classId, RowExclusiveLock);
+	tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(baseId));
+	if (HeapTupleIsValid(tup))
+	{
+		DstOperatorData = (Form_pg_operator) GETSTRUCT(tup);
+
+		/*
+		 * loop over the definition list and extract the information we need.
+		 */
+		foreach(pl, operator_params)
+		{
+			DefElem    *defel = (DefElem *) lfirst(pl);
+			List	   *param = defGetQualifiedName(defel);
+			int			param_type;
+
+			if (pg_strcasecmp(defel->defname, "commutator") == 0)
+				param_type = Anum_pg_operator_oprcom;
+			else if (pg_strcasecmp(defel->defname, "negator") == 0)
+				param_type = Anum_pg_operator_oprnegate;
+			else if (pg_strcasecmp(defel->defname, "restrict") == 0)
+				param_type = Anum_pg_operator_oprrest;
+			else if (pg_strcasecmp(defel->defname, "join") == 0)
+				param_type = Anum_pg_operator_oprjoin;
+			else
+			{
+				ereport(WARNING,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("operator attribute \"%s\" not recognized",
+								defel->defname)));
+				continue;
+			}
+
+			/* Resets if written NULL */
+			if (pg_strcasecmp(NameListToString(param), "null") == 0)
+			{
+				values[param_type - 1] = ObjectIdGetDatum(InvalidOid);
+				replaces[param_type - 1] = true;
+				continue;
+			}
+
+			/*
+			 * Prepare tuple to upgrade the operator
+			 * considering the type of the parameter.
+			 */
+			if (param_type == Anum_pg_operator_oprcom ||
+				param_type == Anum_pg_operator_oprnegate)
+			{
+				otherDefined = true;
+				if (PointerIsValid(param))
+					operator_param_id = OperatorLookup(param,
+												DstOperatorData->oprleft,
+												DstOperatorData->oprright,
+												&otherDefined);
+				else
+					operator_param_id = InvalidOid;
+
+				if (!otherDefined && OidIsValid(operator_param_id)) {
+					ereport(ERROR,
+						(errmsg_internal("You can't set shell (fake) operator")));
+				}
+			}
+			else if (param_type == Anum_pg_operator_oprrest)
+			{
+				if (PointerIsValid(param))
+				{
+					Oid			typeId[5];
+					AclResult	aclresult;
+					typeId[0] = INTERNALOID;	/* PlannerInfo */
+					typeId[1] = OIDOID;		/* operator OID */
+					typeId[2] = INTERNALOID;	/* args list */
+					typeId[3] = INT4OID;	/* varRelid */
+
+					operator_param_id = LookupFuncName(param, 4, typeId, false);
+
+					/* estimators must return float8 */
+					if (get_func_rettype(operator_param_id) != FLOAT8OID)
+						ereport(ERROR,
+								(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+								 errmsg("restriction estimator function %s must return type \"float8\"",
+										NameListToString(param))));
+
+					/* Require EXECUTE rights for the estimator */
+					aclresult = pg_proc_aclcheck(operator_param_id, GetUserId(), ACL_EXECUTE);
+					if (aclresult != ACLCHECK_OK)
+						aclcheck_error(aclresult, ACL_KIND_PROC,
+									   NameListToString(param));
+				}
+				else
+					operator_param_id = 0;
+			}
+			else if (param_type == Anum_pg_operator_oprjoin)
+			{
+				if (PointerIsValid(param))
+				{
+					Oid			typeId[5];
+					AclResult	aclresult;
+					typeId[0] = INTERNALOID;	/* PlannerInfo */
+					typeId[1] = OIDOID;		/* operator OID */
+					typeId[2] = INTERNALOID;	/* args list */
+					typeId[3] = INT2OID;	/* jointype */
+					typeId[4] = INTERNALOID;	/* SpecialJoinInfo */
+
+					/*
+					 * As of Postgres 8.4, the preferred signature for join estimators has
+					 * 5 arguments, but we still allow the old 4-argument form. Try the
+					 * preferred form first.
+					 */
+					operator_param_id = LookupFuncName(param, 5, typeId, true);
+					if (!OidIsValid(operator_param_id))
+						operator_param_id = LookupFuncName(param, 4, typeId, true);
+					/* If not found, reference the 5-argument signature in error msg */
+					if (!OidIsValid(operator_param_id))
+						operator_param_id = LookupFuncName(param, 5, typeId, false);
+
+					/* estimators must return float8 */
+					if (get_func_rettype(operator_param_id) != FLOAT8OID)
+						ereport(ERROR,
+								(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+								 errmsg("restriction estimator function %s must return type \"float8\"",
+										NameListToString(param))));
+
+					/* Require EXECUTE rights for the estimator */
+					aclresult = pg_proc_aclcheck(operator_param_id, GetUserId(), ACL_EXECUTE);
+					if (aclresult != ACLCHECK_OK)
+						aclcheck_error(aclresult, ACL_KIND_PROC,
+									   NameListToString(param));
+				}
+				else
+					operator_param_id = 0;
+			}
+
+			if (OidIsValid(operator_param_id))
+			{
+				values[param_type - 1] = ObjectIdGetDatum(operator_param_id);
+				replaces[param_type - 1] = true;
+			}
+			else
+				ereport(ERROR,
+					(errmsg_internal("Not found function or operator for alter operator")));
+		}
+
+		/* Update heap */
+		tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(baseId));
+		if (HeapTupleIsValid(tup))
+		{
+			tup = heap_modify_tuple(tup,
+									RelationGetDescr(catalog),
+									values,
+									nulls,
+									replaces);
+
+			simple_heap_update(catalog, &tup->t_self, tup);
+			CatalogUpdateIndexes(catalog, tup);
+		}
+	}
+
+	heap_close(catalog, RowExclusiveLock);
+}
\ No newline at end of file
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index b4a1aac..43e0d66 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -320,3 +320,74 @@ RemoveOperatorById(Oid operOid)
 
 	heap_close(relation, RowExclusiveLock);
 }
+
+ObjectAddress
+ExecAlterOperatorStmt(AlterOperatorStmt *stmt)
+{
+	ObjectAddress address;
+	Relation	catalog;
+	Relation	relation;
+	Datum		datum;
+	Oid			ownerId;
+	bool		isnull;
+	HeapTuple	tup;
+
+	/* Address to be modified operator. */
+	address = get_object_address(OBJECT_OPERATOR,
+								 stmt->object,
+								 stmt->objarg,
+								 &relation,
+								 AccessExclusiveLock,
+								 false);
+	Assert(relation == NULL);
+
+	/* Check user rights. */
+	if (!superuser())
+	{
+		AclObjectKind 	aclkind = get_object_aclkind(address.classId);
+		AttrNumber		Anum_name = get_object_attnum_name(address.classId);
+		AttrNumber		Anum_owner = get_object_attnum_owner(address.classId);;
+
+		catalog = heap_open(address.classId, RowExclusiveLock);
+
+		tup = get_catalog_object_by_oid(catalog, address.objectId);
+
+		if (tup == NULL)
+			elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
+				 address.objectId, RelationGetRelationName(catalog));
+
+		datum = heap_getattr(tup, Anum_owner,
+							 RelationGetDescr(catalog), &isnull);
+		Assert(!isnull);
+		ownerId = DatumGetObjectId(datum);
+
+		/* must be owner */
+		if (!has_privs_of_role(GetUserId(), ownerId))
+		{
+			char	   *objname;
+			char		namebuf[NAMEDATALEN];
+
+			if (Anum_name != InvalidAttrNumber)
+			{
+				datum = heap_getattr(tup, Anum_name,
+									 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				objname = NameStr(*DatumGetName(datum));
+			}
+			else
+			{
+				snprintf(namebuf, sizeof(namebuf), "%u",
+						 HeapTupleGetOid(tup));
+				objname = namebuf;
+			}
+			aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
+		}
+		heap_close(catalog, RowExclusiveLock);
+	}
+
+	OperatorUpd(address.classId,
+				address.objectId,
+				stmt->defnames);
+
+	return address;
+}
\ No newline at end of file
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 2d9bf41..44e93b7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3213,6 +3213,19 @@ _copyAlterOwnerStmt(const AlterOwnerStmt *from)
 	return newnode;
 }
 
+static AlterOperatorStmt *
+_copyAlterOperatorStmt(const AlterOperatorStmt *from)
+{
+	AlterOperatorStmt *newnode = makeNode(AlterOperatorStmt);
+
+	COPY_NODE_FIELD(relation);
+	COPY_NODE_FIELD(object);
+	COPY_NODE_FIELD(objarg);
+	COPY_NODE_FIELD(defnames);
+
+	return newnode;
+}
+
 static RuleStmt *
 _copyRuleStmt(const RuleStmt *from)
 {
@@ -4616,6 +4629,9 @@ copyObject(const void *from)
 		case T_AlterOwnerStmt:
 			retval = _copyAlterOwnerStmt(from);
 			break;
+		case T_AlterOperatorStmt:
+			retval = _copyAlterOperatorStmt(from);
+			break;
 		case T_RuleStmt:
 			retval = _copyRuleStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f19251e..017891a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1339,6 +1339,17 @@ _equalAlterOwnerStmt(const AlterOwnerStmt *a, const AlterOwnerStmt *b)
 }
 
 static bool
+_equalAlterOperatorStmt(const AlterOperatorStmt *a, const AlterOperatorStmt *b)
+{
+	COMPARE_NODE_FIELD(relation);
+	COMPARE_NODE_FIELD(object);
+	COMPARE_NODE_FIELD(objarg);
+	COMPARE_NODE_FIELD(defnames);
+
+	return true;
+}
+
+static bool
 _equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
 {
 	COMPARE_NODE_FIELD(relation);
@@ -2980,6 +2991,9 @@ equal(const void *a, const void *b)
 		case T_AlterOwnerStmt:
 			retval = _equalAlterOwnerStmt(a, b);
 			break;
+		case T_AlterOperatorStmt:
+			retval = _equalAlterOperatorStmt(a, b);
+			break;
 		case T_RuleStmt:
 			retval = _equalRuleStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index db6c6f7..3a22717 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -232,7 +232,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		AlterEventTrigStmt
 		AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
 		AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
-		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
+		AlterObjectSchemaStmt AlterOwnerStmt AlterOperatorStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
 		AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
 		AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
 		AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
@@ -769,6 +769,7 @@ stmt :
 			| AlterGroupStmt
 			| AlterObjectSchemaStmt
 			| AlterOwnerStmt
+			| AlterOperatorStmt
 			| AlterPolicyStmt
 			| AlterSeqStmt
 			| AlterSystemStmt
@@ -8198,6 +8199,24 @@ AlterObjectSchemaStmt:
 
 /*****************************************************************************
  *
+ * ALTER OPERATOR name SET define
+ *
+ *****************************************************************************/
+
+AlterOperatorStmt:
+			ALTER OPERATOR any_operator oper_argtypes SET definition
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->defnames = $6;
+					$$ = (Node *)n;
+				}
+		;
+
+
+/*****************************************************************************
+ *
  * ALTER THING name OWNER TO newname
  *
  *****************************************************************************/
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a95eff1..712bb51 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -149,6 +149,7 @@ check_xact_readonly(Node *parsetree)
 		case T_AlterRoleSetStmt:
 		case T_AlterObjectSchemaStmt:
 		case T_AlterOwnerStmt:
+		case T_AlterOperatorStmt:
 		case T_AlterSeqStmt:
 		case T_AlterTableMoveAllStmt:
 		case T_AlterTableStmt:
@@ -861,6 +862,19 @@ standard_ProcessUtility(Node *parsetree,
 			}
 			break;
 
+		case T_AlterOperatorStmt:
+			{
+				AlterOperatorStmt *stmt = (AlterOperatorStmt *) parsetree;
+				if (EventTriggerSupportsObjectType(OBJECT_OPERATOR)) {
+					ProcessUtilitySlow(parsetree, queryString,
+									   context, params,
+									   dest, completionTag);
+				}
+				else
+					ExecAlterOperatorStmt(stmt);
+			}
+			break;
+
 		case T_CommentStmt:
 			{
 				CommentStmt *stmt = (CommentStmt *) parsetree;
@@ -916,12 +930,14 @@ ProcessUtilitySlow(Node *parsetree,
 	ObjectAddress address;
 	ObjectAddress secondaryObject = InvalidObjectAddress;
 
+
 	/* All event trigger calls are done only when isCompleteQuery is true */
 	needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
 
 	/* PG_TRY block is to ensure we call EventTriggerEndCompleteQuery */
 	PG_TRY();
 	{
+
 		if (isCompleteQuery)
 			EventTriggerDDLCommandStart(parsetree);
 
@@ -1478,6 +1494,10 @@ ProcessUtilitySlow(Node *parsetree,
 				address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
 				break;
 
+			case T_AlterOperatorStmt:
+				address = ExecAlterOperatorStmt((AlterOperatorStmt *) parsetree);
+				break;
+
 			case T_CommentStmt:
 				address = CommentObject((CommentStmt *) parsetree);
 				break;
@@ -2491,6 +2511,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "ALTER OPERATOR FAMILY";
 			break;
 
+		case T_AlterOperatorStmt:
+			tag = "ALTER OPERATOR";
+			break;
+
 		case T_AlterTSDictionaryStmt:
 			tag = "ALTER TEXT SEARCH DICTIONARY";
 			break;
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 6e260cb..b5c3040 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1837,4 +1837,8 @@ extern ObjectAddress OperatorCreate(const char *operatorName,
 			   bool canMerge,
 			   bool canHash);
 
+extern void OperatorUpd(Oid classId,
+		Oid baseId,
+		List *operator_params);
+
 #endif   /* PG_OPERATOR_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index d625725..221e094 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -73,6 +73,7 @@ extern void interpret_function_parameter_list(List *parameters,
 /* commands/operatorcmds.c */
 extern ObjectAddress DefineOperator(List *names, List *parameters);
 extern void RemoveOperatorById(Oid operOid);
+extern ObjectAddress ExecAlterOperatorStmt(AlterOperatorStmt *stmt);
 
 /* commands/aggregatecmds.c */
 extern ObjectAddress DefineAggregate(List *name, List *args, bool oldstyle,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 669a0af..b9e9224 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -347,6 +347,7 @@ typedef enum NodeTag
 	T_DropTableSpaceStmt,
 	T_AlterObjectSchemaStmt,
 	T_AlterOwnerStmt,
+	T_AlterOperatorStmt,
 	T_DropOwnedStmt,
 	T_ReassignOwnedStmt,
 	T_CompositeTypeStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 23190e1..ec4cf2c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2543,6 +2543,20 @@ typedef struct AlterOwnerStmt
 
 
 /* ----------------------
+ *		Alter Operator Set Commutator, Negator, Restrict, Join
+ * ----------------------
+ */
+typedef struct AlterOperatorStmt
+{
+	NodeTag		type;
+	RangeVar   *relation;		/* in case it's a table */
+	List	   *object;			/* in case it's some other object */
+	List	   *objarg;			/* argument types, if applicable */
+	List	   *defnames;		/* operator */
+} AlterOperatorStmt;
+
+
+/* ----------------------
  *		Create Rule Statement
  * ----------------------
  */
diff --git a/src/test/regress/expected/alter_operator.out b/src/test/regress/expected/alter_operator.out
new file mode 100644
index 0000000..16438e0
--- /dev/null
+++ b/src/test/regress/expected/alter_operator.out
@@ -0,0 +1,161 @@
+CREATE OR REPLACE FUNCTION fn_op2(boolean, boolean)
+RETURNS boolean AS $$
+    SELECT NULL::BOOLEAN;
+$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR === (
+    LEFTARG = boolean,
+    RIGHTARG = boolean,
+    PROCEDURE = fn_op2,
+    COMMUTATOR = ===,
+    NEGATOR = !==,
+    RESTRICT = contsel,
+    JOIN = contjoinsel,
+    SORT1, SORT2, LTCMP, GTCMP, HASHES, MERGES
+);
+--
+-- Reset and set params
+--
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = NULL);
+SELECT oprcom, oprnegate FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprcom | oprnegate 
+--------+-----------
+      0 |         0
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !=);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = =);
+SELECT oprcom, oprnegate FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprcom | oprnegate 
+--------+-----------
+     85 |        91
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NULL);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprrest | oprjoin 
+---------+---------
+ -       | -
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel);
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprrest |   oprjoin   
+---------+-------------
+ contsel | contjoinsel
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL,
+										   NEGATOR = NULL,
+										   RESTRICT = NULL,
+										   JOIN = NULL);
+SELECT oprcom, oprnegate, oprrest, oprjoin
+FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprcom | oprnegate | oprrest | oprjoin 
+--------+-----------+---------+---------
+      0 |         0 | -       | -
+(1 row)
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !=,
+										   NEGATOR = =,
+										   RESTRICT = contsel,
+										   JOIN = contjoinsel);
+SELECT oprcom, oprnegate, oprrest, oprjoin
+FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+ oprcom | oprnegate | oprrest |   oprjoin   
+--------+-----------+---------+-------------
+     85 |        91 | contsel | contjoinsel
+(1 row)
+
+--
+-- Trying set the wrong parameters
+--
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====);
+ERROR:  Not found function or operator for alter operator
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====);
+ERROR:  Not found function or operator for alter operator
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = blabla);
+ERROR:  function blabla(internal, oid, internal, integer) does not exist
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = blabla);
+ERROR:  function blabla(internal, oid, internal, smallint, internal) does not exist
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==);
+ERROR:  You can't set shell (fake) operator
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==);
+ERROR:  You can't set shell (fake) operator
+--
+-- Trying set params from wrong user
+--
+CREATE USER regression_user;
+SET SESSION AUTHORIZATION regression_user;
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL);
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = NULL);
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NULL);
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NULL);
+ERROR:  must be owner of operator ===
+RESET SESSION AUTHORIZATION;
+DROP USER regression_user;
+DROP OPERATOR === (boolean, boolean);
+--
+-- Trying commutator and negator work
+--
+CREATE TABLE test_ints(i int4);
+CREATE INDEX idx ON test_ints(i);
+INSERT INTO test_ints(i) VALUES (1);
+INSERT INTO test_ints(i) VALUES (2);
+INSERT INTO test_ints(i) VALUES (3);
+set enable_bitmapscan=off;
+set enable_seqscan=off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+               QUERY PLAN               
+----------------------------------------
+ Index Only Scan using idx on test_ints
+   Index Cond: (i < 5)
+(2 rows)
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = NULL);
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+               QUERY PLAN               
+----------------------------------------
+ Index Only Scan using idx on test_ints
+   Filter: (5 > i)
+(2 rows)
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = <);
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+               QUERY PLAN               
+----------------------------------------
+ Index Only Scan using idx on test_ints
+   Index Cond: (i < 5)
+(2 rows)
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = NULL, NEGATOR = NULL);
+-- Negator test
+DROP INDEX idx;
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE NOT (5::int4 > i);
+       QUERY PLAN        
+-------------------------
+ Seq Scan on test_ints
+   Filter: (NOT (5 > i))
+(2 rows)
+
+ALTER OPERATOR > (int4, int4) SET (NEGATOR = <=);
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE NOT (5::int4 > i);
+      QUERY PLAN       
+-----------------------
+ Seq Scan on test_ints
+   Filter: (5 <= i)
+(2 rows)
+
+-- Revert commutator for >
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = <);
+DROP TABLE test_ints;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 91780cd..836f9f9 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml alter_operator
 
 # event triggers cannot run concurrently with any test that runs DDL
 test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index a2e0ceb..fbec844 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -146,6 +146,7 @@ test: without_oid
 test: conversion
 test: truncate
 test: alter_table
+test: alter_operator
 test: sequence
 test: polymorphism
 test: rowtypes
diff --git a/src/test/regress/sql/alter_operator.sql b/src/test/regress/sql/alter_operator.sql
new file mode 100644
index 0000000..e6063b5
--- /dev/null
+++ b/src/test/regress/sql/alter_operator.sql
@@ -0,0 +1,130 @@
+CREATE OR REPLACE FUNCTION fn_op2(boolean, boolean)
+RETURNS boolean AS $$
+    SELECT NULL::BOOLEAN;
+$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR === (
+    LEFTARG = boolean,
+    RIGHTARG = boolean,
+    PROCEDURE = fn_op2,
+    COMMUTATOR = ===,
+    NEGATOR = !==,
+    RESTRICT = contsel,
+    JOIN = contjoinsel,
+    SORT1, SORT2, LTCMP, GTCMP, HASHES, MERGES
+);
+
+
+--
+-- Reset and set params
+--
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = NULL);
+
+SELECT oprcom, oprnegate FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !=);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = =);
+
+SELECT oprcom, oprnegate FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NULL);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = contsel);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = contjoinsel);
+
+SELECT oprrest, oprjoin FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL,
+										   NEGATOR = NULL,
+										   RESTRICT = NULL,
+										   JOIN = NULL);
+
+SELECT oprcom, oprnegate, oprrest, oprjoin
+FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !=,
+										   NEGATOR = =,
+										   RESTRICT = contsel,
+										   JOIN = contjoinsel);
+
+SELECT oprcom, oprnegate, oprrest, oprjoin
+FROM pg_operator WHERE oprname = '===' AND oprleft = 16 AND oprright = 16;
+
+--
+-- Trying set the wrong parameters
+--
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = ====);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = ====);
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = blabla);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = blabla);
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = !==);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = !==);
+
+
+--
+-- Trying set params from wrong user
+--
+
+CREATE USER regression_user;
+SET SESSION AUTHORIZATION regression_user;
+
+ALTER OPERATOR === (boolean, boolean) SET (COMMUTATOR = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (NEGATOR = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (RESTRICT = NULL);
+ALTER OPERATOR === (boolean, boolean) SET (JOIN = NULL);
+
+RESET SESSION AUTHORIZATION;
+DROP USER regression_user;
+
+DROP OPERATOR === (boolean, boolean);
+
+--
+-- Trying commutator and negator work
+--
+
+CREATE TABLE test_ints(i int4);
+CREATE INDEX idx ON test_ints(i);
+INSERT INTO test_ints(i) VALUES (1);
+INSERT INTO test_ints(i) VALUES (2);
+INSERT INTO test_ints(i) VALUES (3);
+
+set enable_bitmapscan=off;
+set enable_seqscan=off;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = NULL);
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = <);
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE 5::int4 > i;
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = NULL, NEGATOR = NULL);
+
+-- Negator test
+
+DROP INDEX idx;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE NOT (5::int4 > i);
+
+ALTER OPERATOR > (int4, int4) SET (NEGATOR = <=);
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM test_ints WHERE NOT (5::int4 > i);
+
+-- Revert commutator for >
+
+ALTER OPERATOR > (int4, int4) SET (COMMUTATOR = <);
+
+DROP TABLE test_ints;
\ No newline at end of file
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index cfd580c..b67afb6 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -70,6 +70,7 @@ AlterFunctionStmt
 AlterObjectSchemaStmt
 AlterOpFamilyStmt
 AlterOwnerStmt
+AlterOperatorStmt
 AlterRoleSetStmt
 AlterRoleStmt
 AlterSeqStmt
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to