]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/operatorcmds.c
Improve internationalization of messages involving type names
[postgresql] / src / backend / commands / operatorcmds.c
index 1c4e5f3bee324a9d75c4f9808e9aadc92af5eb2c..6edbe85ef5328ad10bcf8afc9c37e6bee5399ad4 100644 (file)
@@ -4,12 +4,12 @@
  *
  *       Routines for operator manipulation commands
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
+ *       src/backend/commands/operatorcmds.c
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "catalog/catname.h"
+#include "access/htup_details.h"
 #include "catalog/dependency.h"
-#include "catalog/namespace.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_operator_fn.h"
+#include "catalog/pg_type.h"
+#include "commands/alter.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
+#include "parser/parse_func.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_type.h"
-#include "utils/acl.h"
+#include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/syscache.h"
 
+static Oid     ValidateRestrictionEstimator(List *restrictionName);
+static Oid     ValidateJoinEstimator(List *joinName);
 
 /*
  * DefineOperator
  *
  * 'parameters' is a list of DefElem
  */
-void
+ObjectAddress
 DefineOperator(List *names, List *parameters)
 {
        char       *oprName;
        Oid                     oprNamespace;
        AclResult       aclresult;
-       uint16          precedence = 0;                 /* operator precedence */
-       bool            canHash = false;                /* operator hashes */
        bool            canMerge = false;               /* operator merges */
-       bool            isLeftAssociative = true;               /* operator is left
-                                                                                                * associative */
+       bool            canHash = false;        /* operator hashes */
        List       *functionName = NIL;         /* function for operator */
        TypeName   *typeName1 = NULL;           /* first type name */
        TypeName   *typeName2 = NULL;           /* second type name */
        Oid                     typeId1 = InvalidOid;   /* types converted to OID */
        Oid                     typeId2 = InvalidOid;
-       List       *commutatorName = NIL;       /* optional commutator operator
-                                                                                * name */
+       Oid                     rettype;
+       List       *commutatorName = NIL;       /* optional commutator operator name */
        List       *negatorName = NIL;          /* optional negator operator name */
-       List       *restrictionName = NIL;      /* optional restrict. sel.
-                                                                                * procedure */
-       List       *joinName = NIL;                     /* optional join sel. procedure */
-       List       *leftSortName = NIL;         /* optional left sort operator */
-       List       *rightSortName = NIL;        /* optional right sort operator */
-       List       *ltCompareName = NIL;        /* optional < compare operator */
-       List       *gtCompareName = NIL;        /* optional > compare operator */
-       List       *pl;
+       List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
+       List       *joinName = NIL; /* optional join sel. procedure */
+       Oid                     functionOid;    /* functions converted to OID */
+       Oid                     restrictionOid;
+       Oid                     joinOid;
+       Oid                     typeId[2];              /* to hold left and right arg */
+       int                     nargs;
+       ListCell   *pl;
 
        /* Convert list of names to a name and namespace */
        oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
@@ -90,7 +95,8 @@ DefineOperator(List *names, List *parameters)
        /* Check we have creation rights in target namespace */
        aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
        if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, get_namespace_name(oprNamespace));
+               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                          get_namespace_name(oprNamespace));
 
        /*
         * loop over the definition list and extract the information we need.
@@ -99,54 +105,52 @@ DefineOperator(List *names, List *parameters)
        {
                DefElem    *defel = (DefElem *) lfirst(pl);
 
-               if (strcasecmp(defel->defname, "leftarg") == 0)
+               if (pg_strcasecmp(defel->defname, "leftarg") == 0)
                {
                        typeName1 = defGetTypeName(defel);
                        if (typeName1->setof)
-                               elog(ERROR, "setof type not implemented for leftarg");
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                       errmsg("SETOF type not allowed for operator argument")));
                }
-               else if (strcasecmp(defel->defname, "rightarg") == 0)
+               else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
                {
                        typeName2 = defGetTypeName(defel);
                        if (typeName2->setof)
-                               elog(ERROR, "setof type not implemented for rightarg");
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                       errmsg("SETOF type not allowed for operator argument")));
                }
-               else if (strcasecmp(defel->defname, "procedure") == 0)
+               else if (pg_strcasecmp(defel->defname, "procedure") == 0)
                        functionName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "precedence") == 0)
-               {
-                       /* NOT IMPLEMENTED (never worked in v4.2) */
-                       elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
-               }
-               else if (strcasecmp(defel->defname, "associativity") == 0)
-               {
-                       /* NOT IMPLEMENTED (never worked in v4.2) */
-                       elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
-               }
-               else if (strcasecmp(defel->defname, "commutator") == 0)
+               else if (pg_strcasecmp(defel->defname, "commutator") == 0)
                        commutatorName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "negator") == 0)
+               else if (pg_strcasecmp(defel->defname, "negator") == 0)
                        negatorName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "restrict") == 0)
+               else if (pg_strcasecmp(defel->defname, "restrict") == 0)
                        restrictionName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "join") == 0)
+               else if (pg_strcasecmp(defel->defname, "join") == 0)
                        joinName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "hashes") == 0)
-                       canHash = TRUE;
-               else if (strcasecmp(defel->defname, "merges") == 0)
-                       canMerge = TRUE;
-               else if (strcasecmp(defel->defname, "sort1") == 0)
-                       leftSortName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "sort2") == 0)
-                       rightSortName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "ltcmp") == 0)
-                       ltCompareName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "gtcmp") == 0)
-                       gtCompareName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "hashes") == 0)
+                       canHash = defGetBoolean(defel);
+               else if (pg_strcasecmp(defel->defname, "merges") == 0)
+                       canMerge = defGetBoolean(defel);
+               /* These obsolete options are taken as meaning canMerge */
+               else if (pg_strcasecmp(defel->defname, "sort1") == 0)
+                       canMerge = true;
+               else if (pg_strcasecmp(defel->defname, "sort2") == 0)
+                       canMerge = true;
+               else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
+                       canMerge = true;
+               else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
+                       canMerge = true;
                else
                {
-                       elog(WARNING, "DefineOperator: attribute \"%s\" not recognized",
-                                defel->defname);
+                       /* WARNING, not ERROR, for historical backwards-compatibility */
+                       ereport(WARNING,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("operator attribute \"%s\" not recognized",
+                                                       defel->defname)));
                }
        }
 
@@ -154,98 +158,179 @@ DefineOperator(List *names, List *parameters)
         * make sure we have our required definitions
         */
        if (functionName == NIL)
-               elog(ERROR, "Define: \"procedure\" unspecified");
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("operator procedure must be specified")));
 
        /* Transform type names to type OIDs */
        if (typeName1)
-               typeId1 = typenameTypeId(typeName1);
+               typeId1 = typenameTypeId(NULL, typeName1);
        if (typeName2)
-               typeId2 = typenameTypeId(typeName2);
+               typeId2 = typenameTypeId(NULL, typeName2);
+
+       if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                  errmsg("at least one of leftarg or rightarg must be specified")));
+
+       if (typeName1)
+       {
+               aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error_type(aclresult, typeId1);
+       }
+
+       if (typeName2)
+       {
+               aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error_type(aclresult, typeId2);
+       }
 
        /*
-        * If any of the mergejoin support operators were given, then canMerge
-        * is implicit.  If canMerge is specified or implicit, fill in default
-        * operator names for any missing mergejoin support operators.
+        * Look up the operator's underlying function.
         */
-       if (leftSortName || rightSortName || ltCompareName || gtCompareName)
-               canMerge = true;
-
-       if (canMerge)
+       if (!OidIsValid(typeId1))
+       {
+               typeId[0] = typeId2;
+               nargs = 1;
+       }
+       else if (!OidIsValid(typeId2))
+       {
+               typeId[0] = typeId1;
+               nargs = 1;
+       }
+       else
        {
-               if (!leftSortName)
-                       leftSortName = makeList1(makeString("<"));
-               if (!rightSortName)
-                       rightSortName = makeList1(makeString("<"));
-               if (!ltCompareName)
-                       ltCompareName = makeList1(makeString("<"));
-               if (!gtCompareName)
-                       gtCompareName = makeList1(makeString(">"));
+               typeId[0] = typeId1;
+               typeId[1] = typeId2;
+               nargs = 2;
        }
+       functionOid = LookupFuncName(functionName, nargs, typeId, false);
+
+       /*
+        * We require EXECUTE rights for the function.  This isn't strictly
+        * necessary, since EXECUTE will be checked at any attempted use of the
+        * operator, but it seems like a good idea anyway.
+        */
+       aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC,
+                                          NameListToString(functionName));
+
+       rettype = get_func_rettype(functionOid);
+       aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error_type(aclresult, rettype);
+
+       /*
+        * Look up restriction and join estimators if specified
+        */
+       if (restrictionName)
+               restrictionOid = ValidateRestrictionEstimator(restrictionName);
+       else
+               restrictionOid = InvalidOid;
+       if (joinName)
+               joinOid = ValidateJoinEstimator(joinName);
+       else
+               joinOid = InvalidOid;
 
        /*
         * now have OperatorCreate do all the work..
         */
-       OperatorCreate(oprName,         /* operator name */
-                                  oprNamespace, /* namespace */
-                                  typeId1,             /* left type id */
-                                  typeId2,             /* right type id */
-                                  functionName,        /* function for operator */
-                                  precedence,  /* operator precedence */
-                                  isLeftAssociative,   /* operator is left associative */
-                                  commutatorName,              /* optional commutator operator
-                                                                                * name */
-                                  negatorName, /* optional negator operator name */
-                                  restrictionName,             /* optional restrict. sel.
-                                                                                * procedure */
-                                  joinName,    /* optional join sel. procedure name */
-                                  canHash,             /* operator hashes */
-                                  leftSortName,        /* optional left sort operator */
-                                  rightSortName,       /* optional right sort operator */
-                                  ltCompareName,       /* optional < comparison op */
-                                  gtCompareName);      /* optional < comparison op */
+       return
+               OperatorCreate(oprName, /* operator name */
+                                          oprNamespace,        /* namespace */
+                                          typeId1, /* left type id */
+                                          typeId2, /* right type id */
+                                          functionOid,         /* function for operator */
+                                          commutatorName,      /* optional commutator operator name */
+                                          negatorName,         /* optional negator operator name */
+                                          restrictionOid,      /* optional restrict. sel. procedure */
+                                          joinOid, /* optional join sel. procedure name */
+                                          canMerge,    /* operator merges */
+                                          canHash);    /* operator hashes */
 }
 
-
 /*
- * RemoveOperator
- *             Deletes an operator.
+ * Look up a restriction estimator function ny name, and verify that it has
+ * the correct signature and we have the permissions to attach it to an
+ * operator.
  */
-void
-RemoveOperator(RemoveOperStmt *stmt)
+static Oid
+ValidateRestrictionEstimator(List *restrictionName)
 {
-       List *operatorName = stmt->opname;
-       TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
-       TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
-       Oid                     operOid;
-       HeapTuple       tup;
-       ObjectAddress object;
+       Oid                     typeId[4];
+       Oid                     restrictionOid;
+       AclResult       aclresult;
 
-       operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
-                                                                         "RemoveOperator");
+       typeId[0] = INTERNALOID;        /* PlannerInfo */
+       typeId[1] = OIDOID;                     /* operator OID */
+       typeId[2] = INTERNALOID;        /* args list */
+       typeId[3] = INT4OID;            /* varRelid */
 
-       tup = SearchSysCache(OPEROID,
-                                                ObjectIdGetDatum(operOid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))     /* should not happen */
-               elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
-                        NameListToString(operatorName));
+       restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
 
-       /* Permission check: must own operator or its namespace */
-       if (!pg_oper_ownercheck(operOid, GetUserId()) &&
-               !pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace,
-                                                                GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
+       /* estimators must return float8 */
+       if (get_func_rettype(restrictionOid) != FLOAT8OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("restriction estimator function %s must return type \"%s\"",
+                                               NameListToString(restrictionName), "float8")));
 
-       ReleaseSysCache(tup);
+       /* Require EXECUTE rights for the estimator */
+       aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC,
+                                          NameListToString(restrictionName));
+
+       return restrictionOid;
+}
+
+/*
+ * Look up a join estimator function ny name, and verify that it has the
+ * correct signature and we have the permissions to attach it to an
+ * operator.
+ */
+static Oid
+ValidateJoinEstimator(List *joinName)
+{
+       Oid                     typeId[5];
+       Oid                     joinOid;
+       AclResult       aclresult;
+
+       typeId[0] = INTERNALOID;        /* PlannerInfo */
+       typeId[1] = OIDOID;                     /* operator OID */
+       typeId[2] = INTERNALOID;        /* args list */
+       typeId[3] = INT2OID;            /* jointype */
+       typeId[4] = INTERNALOID;        /* SpecialJoinInfo */
 
        /*
-        * Do the deletion
+        * 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.
         */
-       object.classId = get_system_catalog_relid(OperatorRelationName);
-       object.objectId = operOid;
-       object.objectSubId = 0;
+       joinOid = LookupFuncName(joinName, 5, typeId, true);
+       if (!OidIsValid(joinOid))
+               joinOid = LookupFuncName(joinName, 4, typeId, true);
+       /* If not found, reference the 5-argument signature in error msg */
+       if (!OidIsValid(joinOid))
+               joinOid = LookupFuncName(joinName, 5, typeId, false);
+
+       /* estimators must return float8 */
+       if (get_func_rettype(joinOid) != FLOAT8OID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                        errmsg("join estimator function %s must return type \"%s\"",
+                                       NameListToString(joinName), "float8")));
 
-       performDeletion(&object, stmt->behavior);
+       /* Require EXECUTE rights for the estimator */
+       aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC,
+                                          NameListToString(joinName));
+
+       return joinOid;
 }
 
 /*
@@ -256,15 +341,32 @@ RemoveOperatorById(Oid operOid)
 {
        Relation        relation;
        HeapTuple       tup;
+       Form_pg_operator op;
+
+       relation = heap_open(OperatorRelationId, RowExclusiveLock);
 
-       relation = heap_openr(OperatorRelationName, RowExclusiveLock);
+       tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
+       if (!HeapTupleIsValid(tup)) /* should not happen */
+               elog(ERROR, "cache lookup failed for operator %u", operOid);
+       op = (Form_pg_operator) GETSTRUCT(tup);
 
-       tup = SearchSysCache(OPEROID,
-                                                ObjectIdGetDatum(operOid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))     /* should not happen */
-               elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u",
-                        operOid);
+       /*
+        * Reset links from commutator and negator, if any.  In case of a
+        * self-commutator or self-negator, this means we have to re-fetch the
+        * updated tuple.  (We could optimize away updates on the tuple we're
+        * about to drop, but it doesn't seem worth convoluting the logic for.)
+        */
+       if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
+       {
+               OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
+               if (operOid == op->oprcom || operOid == op->oprnegate)
+               {
+                       ReleaseSysCache(tup);
+                       tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
+                       if (!HeapTupleIsValid(tup)) /* should not happen */
+                               elog(ERROR, "cache lookup failed for operator %u", operOid);
+               }
+       }
 
        simple_heap_delete(relation, &tup->t_self);
 
@@ -272,3 +374,158 @@ RemoveOperatorById(Oid operOid)
 
        heap_close(relation, RowExclusiveLock);
 }
+
+/*
+ * AlterOperator
+ *             routine implementing ALTER OPERATOR <operator> SET (option = ...).
+ *
+ * Currently, only RESTRICT and JOIN estimator functions can be changed.
+ */
+ObjectAddress
+AlterOperator(AlterOperatorStmt *stmt)
+{
+       ObjectAddress address;
+       Oid                     oprId;
+       Relation        catalog;
+       HeapTuple       tup;
+       Form_pg_operator oprForm;
+       int                     i;
+       ListCell   *pl;
+       Datum           values[Natts_pg_operator];
+       bool            nulls[Natts_pg_operator];
+       bool            replaces[Natts_pg_operator];
+       List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
+       bool            updateRestriction = false;
+       Oid                     restrictionOid;
+       List       *joinName = NIL; /* optional join sel. procedure */
+       bool            updateJoin = false;
+       Oid                     joinOid;
+
+       /* Look up the operator */
+       oprId = LookupOperNameTypeNames(NULL, stmt->opername,
+                                                                       (TypeName *) linitial(stmt->operargs),
+                                                                       (TypeName *) lsecond(stmt->operargs),
+                                                                       false, -1);
+       catalog = heap_open(OperatorRelationId, RowExclusiveLock);
+       tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
+       if (tup == NULL)
+               elog(ERROR, "cache lookup failed for operator %u", oprId);
+       oprForm = (Form_pg_operator) GETSTRUCT(tup);
+
+       /* Process options */
+       foreach(pl, stmt->options)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+               List       *param;
+
+               if (defel->arg == NULL)
+                       param = NIL;            /* NONE, removes the function */
+               else
+                       param = defGetQualifiedName(defel);
+
+               if (pg_strcasecmp(defel->defname, "restrict") == 0)
+               {
+                       restrictionName = param;
+                       updateRestriction = true;
+               }
+               else if (pg_strcasecmp(defel->defname, "join") == 0)
+               {
+                       joinName = param;
+                       updateJoin = true;
+               }
+
+               /*
+                * The rest of the options that CREATE accepts cannot be changed.
+                * Check for them so that we can give a meaningful error message.
+                */
+               else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
+                                pg_strcasecmp(defel->defname, "rightarg") == 0 ||
+                                pg_strcasecmp(defel->defname, "procedure") == 0 ||
+                                pg_strcasecmp(defel->defname, "commutator") == 0 ||
+                                pg_strcasecmp(defel->defname, "negator") == 0 ||
+                                pg_strcasecmp(defel->defname, "hashes") == 0 ||
+                                pg_strcasecmp(defel->defname, "merges") == 0)
+               {
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("operator attribute \"%s\" can not be changed",
+                                                       defel->defname)));
+               }
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("operator attribute \"%s\" not recognized",
+                                                       defel->defname)));
+       }
+
+       /* Check permissions. Must be owner. */
+       if (!pg_oper_ownercheck(oprId, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+                                          NameStr(oprForm->oprname));
+
+       /*
+        * Look up restriction and join estimators if specified
+        */
+       if (restrictionName)
+               restrictionOid = ValidateRestrictionEstimator(restrictionName);
+       else
+               restrictionOid = InvalidOid;
+       if (joinName)
+               joinOid = ValidateJoinEstimator(joinName);
+       else
+               joinOid = InvalidOid;
+
+       /* Perform additional checks, like OperatorCreate does */
+       if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
+       {
+               /* If it's not a binary op, these things mustn't be set: */
+               if (OidIsValid(joinOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("only binary operators can have join selectivity")));
+       }
+
+       if (oprForm->oprresult != BOOLOID)
+       {
+               if (OidIsValid(restrictionOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can have restriction selectivity")));
+               if (OidIsValid(joinOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                               errmsg("only boolean operators can have join selectivity")));
+       }
+
+       /* Update the tuple */
+       for (i = 0; i < Natts_pg_operator; ++i)
+       {
+               values[i] = (Datum) 0;
+               replaces[i] = false;
+               nulls[i] = false;
+       }
+       if (updateRestriction)
+       {
+               replaces[Anum_pg_operator_oprrest - 1] = true;
+               values[Anum_pg_operator_oprrest - 1] = restrictionOid;
+       }
+       if (updateJoin)
+       {
+               replaces[Anum_pg_operator_oprjoin - 1] = true;
+               values[Anum_pg_operator_oprjoin - 1] = joinOid;
+       }
+
+       tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
+                                                       values, nulls, replaces);
+
+       simple_heap_update(catalog, &tup->t_self, tup);
+       CatalogUpdateIndexes(catalog, tup);
+
+       address = makeOperatorDependencies(tup, true);
+
+       InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
+
+       heap_close(catalog, NoLock);
+
+       return address;
+}