diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 072f530..7ff95c1 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -30,6 +30,7 @@
 #include "catalog/pg_type.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 +54,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 +564,7 @@ OperatorCreate(const char *operatorName,
 		commutatorId = operatorObjectId;
 
 	if (OidIsValid(commutatorId) || OidIsValid(negatorId))
-		OperatorUpd(operatorObjectId, commutatorId, negatorId);
+		ShellOperatorUpd(operatorObjectId, commutatorId, negatorId);
 
 	return address;
 }
@@ -633,7 +634,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 +643,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 +865,164 @@ makeOperatorDependencies(HeapTuple tuple)
 
 	return myself;
 }
+
+/*
+ * Operator update aka ALTER OPERATOR for COMMUTATOR, NEGATOR, RESTRICT, JOIN
+ */
+void OperatorUpd(Oid classId,
+				 Oid baseId,
+				 List *operator_param,
+				 unsigned int operator_param_type)
+{
+	int			i;
+	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);
+
+		/*
+		 * Prepare tuple to upgrade the operator
+		 * considering the type of the parameter.
+		 */
+		if (operator_param_type == Anum_pg_operator_oprcom ||
+			operator_param_type == Anum_pg_operator_oprnegate)
+		{
+			otherDefined = true;
+			if (PointerIsValid(operator_param))
+				operator_param_id = OperatorLookup(operator_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 (operator_param_type == Anum_pg_operator_oprrest)
+		{
+			/* Resets if written NONE */
+			if (pg_strcasecmp(NameListToString(operator_param), "none") == 0)
+				operator_param = NULL;
+
+			if (PointerIsValid(operator_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(operator_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(operator_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(operator_param));
+			}
+			else
+				operator_param_id = 0;
+		}
+		else if (operator_param_type == Anum_pg_operator_oprjoin)
+		{
+			/* Resets if written NONE */
+			if (pg_strcasecmp(NameListToString(operator_param), "none") == 0)
+				operator_param = NULL;
+
+			if (PointerIsValid(operator_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(operator_param, 5, typeId, true);
+				if (!OidIsValid(operator_param_id))
+					operator_param_id = LookupFuncName(operator_param, 4, typeId, true);
+				/* If not found, reference the 5-argument signature in error msg */
+				if (!OidIsValid(operator_param_id))
+					operator_param_id = LookupFuncName(operator_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(operator_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(operator_param));
+			}
+			else
+				operator_param_id = 0;
+		}
+
+		/* Update heap */
+		if (OidIsValid(operator_param_id) || !PointerIsValid(operator_param))
+		{
+			tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(baseId));
+			if (HeapTupleIsValid(tup))
+			{
+				values[operator_param_type - 1] = ObjectIdGetDatum(operator_param_id);
+				replaces[operator_param_type - 1] = true;
+
+				tup = heap_modify_tuple(tup,
+										RelationGetDescr(catalog),
+										values,
+										nulls,
+										replaces);
+
+				simple_heap_update(catalog, &tup->t_self, tup);
+
+				CatalogUpdateIndexes(catalog, tup);
+
+				values[operator_param_type - 1] = (Datum) NULL;
+				replaces[operator_param_type - 1] = false;
+			}
+		}
+		else
+			ereport(ERROR,
+					(errmsg_internal("Not found function or operator for alter operator")));
+	}
+
+	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..73ca2c6 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -320,3 +320,100 @@ 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);
+	}
+
+	/* Select parameter for change */
+	if (pg_strcasecmp(stmt->cmd_name, "commutator")  == 0)
+	{
+		OperatorUpd(address.classId,
+				address.objectId,
+				stmt->oprparam,
+				Anum_pg_operator_oprcom);
+	}
+	else if (pg_strcasecmp(stmt->cmd_name, "negator")  == 0)
+	{
+		OperatorUpd(address.classId,
+				address.objectId,
+				stmt->oprparam,
+				Anum_pg_operator_oprnegate);
+	}
+	else if (pg_strcasecmp(stmt->cmd_name, "restrict")  == 0)
+	{
+		OperatorUpd(address.classId,
+				address.objectId,
+				stmt->oprparam,
+				Anum_pg_operator_oprrest);
+	}
+	else if (pg_strcasecmp(stmt->cmd_name, "join")  == 0)
+	{
+		OperatorUpd(address.classId,
+				address.objectId,
+				stmt->oprparam,
+				Anum_pg_operator_oprjoin);
+	}
+
+	return address;
+}
\ No newline at end of file
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 76b63af..aa6796d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3130,6 +3130,21 @@ _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(oprparam);
+	COPY_STRING_FIELD(cmd_name);
+	COPY_SCALAR_FIELD(missing_ok);
+
+	return newnode;
+}
+
 static RuleStmt *
 _copyRuleStmt(const RuleStmt *from)
 {
@@ -4526,6 +4541,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 e032142..06e4ba0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1324,6 +1324,19 @@ _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(oprparam);
+	COMPARE_STRING_FIELD(cmd_name);
+	COMPARE_SCALAR_FIELD(missing_ok);
+
+	return true;
+}
+
+static bool
 _equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
 {
 	COMPARE_NODE_FIELD(relation);
@@ -2920,6 +2933,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 e71d926..ec6c08d 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
@@ -561,7 +561,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
-	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
+	COMMITTED COMMUTATOR CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
 	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
 	CROSS CSV CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
@@ -597,7 +597,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
 
-	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
+	NAME_P NAMES NATIONAL NATURAL NCHAR NEGATOR NEXT NO NONE
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
 	NULLS_P NUMERIC
 
@@ -755,6 +755,7 @@ stmt :
 			| AlterGroupStmt
 			| AlterObjectSchemaStmt
 			| AlterOwnerStmt
+			| AlterOperatorStmt
 			| AlterPolicyStmt
 			| AlterSeqStmt
 			| AlterSystemStmt
@@ -8173,6 +8174,76 @@ AlterObjectSchemaStmt:
 
 /*****************************************************************************
  *
+ * ALTER OPERATOR name SET THINGS name
+ *
+ *****************************************************************************/
+
+AlterOperatorStmt:
+			ALTER OPERATOR any_operator oper_argtypes SET COMMUTATOR any_operator
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = $7;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER OPERATOR any_operator oper_argtypes SET COMMUTATOR NONE
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = NULL;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER OPERATOR any_operator oper_argtypes SET NEGATOR any_operator
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = $7;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER OPERATOR any_operator oper_argtypes SET NEGATOR NONE
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = NULL;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER OPERATOR any_operator oper_argtypes SET RESTRICT handler_name
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = $7;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+			| ALTER OPERATOR any_operator oper_argtypes SET JOIN handler_name
+				{
+					AlterOperatorStmt *n = makeNode(AlterOperatorStmt);
+					n->object = $3;
+					n->objarg = $4;
+					n->oprparam = $7;
+					n->cmd_name = $6;
+					n->missing_ok = false;
+					$$ = (Node *)n;
+				}
+		;
+
+
+/*****************************************************************************
+ *
  * ALTER THING name OWNER TO newname
  *
  *****************************************************************************/
@@ -13464,6 +13535,7 @@ unreserved_keyword:
 			| COMMENTS
 			| COMMIT
 			| COMMITTED
+			| COMMUTATOR
 			| CONFIGURATION
 			| CONFLICT
 			| CONNECTION
@@ -13567,6 +13639,7 @@ unreserved_keyword:
 			| MOVE
 			| NAME_P
 			| NAMES
+			| NEGATOR
 			| NEXT
 			| NO
 			| NOTHING
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 78bfd34..d8b6bd7 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 e22eb27..4af96fa 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1825,4 +1825,9 @@ extern ObjectAddress OperatorCreate(const char *operatorName,
 			   bool canMerge,
 			   bool canHash);
 
+extern void OperatorUpd(Oid classId,
+		Oid baseId,
+		List *operator_param,
+		unsigned int operator_param_type);
+
 #endif   /* PG_OPERATOR_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index c3a1748..c7df5ff 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -72,6 +72,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 768f413..d9f0eb4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -343,6 +343,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 556c1c5..352c89c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2437,6 +2437,22 @@ 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	   *oprparam;		/* operator */
+	const char	   *cmd_name;	/* COMMUTATOR, NEGATOR, RESTRICT, JOIN */
+	bool		missing_ok;		/* skip error if missing? */
+} AlterOperatorStmt;
+
+
+/* ----------------------
  *		Create Rule Statement
  * ----------------------
  */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index faea991..3abb90d 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -85,6 +85,7 @@ PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
 PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("commutator", COMMUTATOR, UNRESERVED_KEYWORD)
 PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
@@ -247,6 +248,7 @@ PG_KEYWORD("names", NAMES, UNRESERVED_KEYWORD)
 PG_KEYWORD("national", NATIONAL, COL_NAME_KEYWORD)
 PG_KEYWORD("natural", NATURAL, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("nchar", NCHAR, COL_NAME_KEYWORD)
+PG_KEYWORD("negator", NEGATOR, UNRESERVED_KEYWORD)
 PG_KEYWORD("next", NEXT, UNRESERVED_KEYWORD)
 PG_KEYWORD("no", NO, UNRESERVED_KEYWORD)
 PG_KEYWORD("none", NONE, COL_NAME_KEYWORD)
diff --git a/src/test/regress/expected/alter_operator.out b/src/test/regress/expected/alter_operator.out
new file mode 100644
index 0000000..9282ae0
--- /dev/null
+++ b/src/test/regress/expected/alter_operator.out
@@ -0,0 +1,117 @@
+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 NONE;
+ALTER OPERATOR === (boolean, boolean) SET NEGATOR NONE;
+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 NONE;
+ALTER OPERATOR === (boolean, boolean) SET JOIN NONE;
+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)
+
+--
+-- 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 NONE;
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET NEGATOR NONE;
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET RESTRICT NONE;
+ERROR:  must be owner of operator ===
+ALTER OPERATOR === (boolean, boolean) SET JOIN NONE;
+ERROR:  must be owner of operator ===
+RESET SESSION AUTHORIZATION;
+DROP USER regression_user;
+DROP OPERATOR === (boolean, boolean);
+--
+-- Trying commutator 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 NONE;
+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)
+
+DROP TABLE test_ints;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b0ebb6b..993f094 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 8409c0f..b52030c 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -145,6 +145,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..4df74f5
--- /dev/null
+++ b/src/test/regress/sql/alter_operator.sql
@@ -0,0 +1,97 @@
+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 NONE;
+ALTER OPERATOR === (boolean, boolean) SET NEGATOR NONE;
+
+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 NONE;
+ALTER OPERATOR === (boolean, boolean) SET JOIN NONE;
+
+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;
+
+
+--
+-- 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 NONE;
+ALTER OPERATOR === (boolean, boolean) SET NEGATOR NONE;
+ALTER OPERATOR === (boolean, boolean) SET RESTRICT NONE;
+ALTER OPERATOR === (boolean, boolean) SET JOIN NONE;
+
+RESET SESSION AUTHORIZATION;
+DROP USER regression_user;
+
+DROP OPERATOR === (boolean, boolean);
+
+--
+-- Trying commutator 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 NONE;
+
+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;
+
+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
