Hello Hackers.

I have attached a patch that extends ALTER OPERATOR to support COMMUTATOR, 
NEGATOR, RESTRICT and JOIN. This patch is based on master. It is small patch 
with regression tests.

Why do it?

The operator has four important parameters that can be set only during the 
creation. These are: commutator (oprcom), negator (oprnegate), restrict 
(oprrest), join (oprjoin). For example, you created operator with RESTRICT = 
contsel . After a while you began to actively use your new operator. Then you 
develop a new function for RESTRICT (my_restrict_func).   To change the 
RESTRICT operator you have to create a new database and to migrate there 
because operator is used and you can't DROP OPERATOR and CREATE OPERATOR 
again. The fact that it is extremely difficult sometimes almost think it is 
clear to all.
It is interesting that the change in the parameters of the operator takes 
place periodically for the built-in operators (when changing major version), 
but it is impossible for users defined operators.
Real life example is intarray ( 
http://www.postgresql.org/message-id/capphfdssy+qepdcovxx-b4lp3ybr+qs04m6-arggknfk3fr...@mail.gmail.com
 ). 
Also using ALTER OPERATOR for self-linkage more logical than make operator 
shells.

Simple 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 NONE;
ALTER OPERATOR === (boolean, boolean) SET NEGATOR NONE;
ALTER OPERATOR === (boolean, boolean) SET RESTRICT NONE;
ALTER OPERATOR === (boolean, boolean) SET JOIN NONE;

It seems to me a syntax similar to the classic ALTER will be better than what 
was used in the CREATE OPERATOR.

In this patch I am:
1. renamed OperatorUpd to ShellOperatorUpd. It more right name.
2. created AlterOperatorStmt struct for parsing command.
3. created ExecAlterOperatorStmt function for check user rights and select 
parameter for edit. 
4. recreated OperatorUpd for update params of operator in catalog.
5. And other small fix for extend parser. 

In AlterOperatorStmt confuses me to use const char for cmd_name. In addition, 
I clean only the catalog cache but judging by how works shell operators, 
nothing more is needed.

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..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
-- 
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