]> 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 36bedf4f48e580723076b0a9d32555a438782acc..6edbe85ef5328ad10bcf8afc9c37e6bee5399ad4 100644 (file)
@@ -4,7 +4,7 @@
  *
  *       Routines for operator manipulation commands
  *
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/htup_details.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
-#include "catalog/namespace.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 void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
+static Oid     ValidateRestrictionEstimator(List *restrictionName);
+static Oid     ValidateJoinEstimator(List *joinName);
 
 /*
  * DefineOperator
@@ -61,7 +64,7 @@ static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerI
  *
  * 'parameters' is a list of DefElem
  */
-void
+ObjectAddress
 DefineOperator(List *names, List *parameters)
 {
        char       *oprName;
@@ -74,6 +77,7 @@ DefineOperator(List *names, List *parameters)
        TypeName   *typeName2 = NULL;           /* second type name */
        Oid                     typeId1 = InvalidOid;   /* types converted to OID */
        Oid                     typeId2 = InvalidOid;
+       Oid                     rettype;
        List       *commutatorName = NIL;       /* optional commutator operator name */
        List       *negatorName = NIL;          /* optional negator operator name */
        List       *restrictionName = NIL;      /* optional restrict. sel. procedure */
@@ -81,23 +85,13 @@ DefineOperator(List *names, List *parameters)
        Oid                     functionOid;    /* functions converted to OID */
        Oid                     restrictionOid;
        Oid                     joinOid;
-       Oid                     typeId[5];              /* only need up to 5 args here */
+       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);
 
-       /*
-        * The SQL standard committee has decided that => should be used for named
-        * parameters; therefore, a future release of PostgreSQL may disallow it
-        * as the name of a user-defined operator.
-        */
-       if (strcmp(oprName, "=>") == 0)
-               ereport(WARNING,
-                               (errmsg("=> is deprecated as an operator name"),
-                                errdetail("This name may be disallowed altogether in future versions of PostgreSQL.")));
-
        /* Check we have creation rights in target namespace */
        aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
        if (aclresult != ACLCHECK_OK)
@@ -151,10 +145,13 @@ DefineOperator(List *names, List *parameters)
                else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
                        canMerge = true;
                else
+               {
+                       /* WARNING, not ERROR, for historical backwards-compatibility */
                        ereport(WARNING,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("operator attribute \"%s\" not recognized",
                                                        defel->defname)));
+               }
        }
 
        /*
@@ -167,15 +164,29 @@ DefineOperator(List *names, List *parameters)
 
        /* Transform type names to type OIDs */
        if (typeName1)
-               typeId1 = typenameTypeId(NULL, typeName1, NULL);
+               typeId1 = typenameTypeId(NULL, typeName1);
        if (typeName2)
-               typeId2 = typenameTypeId(NULL, typeName2, NULL);
+               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);
+       }
+
        /*
         * Look up the operator's underlying function.
         */
@@ -198,7 +209,7 @@ DefineOperator(List *names, List *parameters)
        functionOid = LookupFuncName(functionName, nargs, typeId, false);
 
        /*
-        * We require EXECUTE rights for the function.  This isn't strictly
+        * 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.
         */
@@ -207,138 +218,119 @@ DefineOperator(List *names, List *parameters)
                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 estimator if specified
+        * Look up restriction and join estimators if specified
         */
        if (restrictionName)
-       {
-               typeId[0] = INTERNALOID;        /* PlannerInfo */
-               typeId[1] = OIDOID;             /* operator OID */
-               typeId[2] = INTERNALOID;        /* args list */
-               typeId[3] = INT4OID;    /* varRelid */
-
-               restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
-
-               /* 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 \"float8\"",
-                                                       NameListToString(restrictionName))));
-
-               /* 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));
-       }
+               restrictionOid = ValidateRestrictionEstimator(restrictionName);
        else
                restrictionOid = InvalidOid;
-
-       /*
-        * Look up join estimator if specified
-        */
        if (joinName)
-       {
-               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.
-                */
-               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 \"float8\"",
-                                       NameListToString(joinName))));
-
-               /* 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));
-       }
+               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 */
-                                  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 */
+       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(RemoveFuncStmt *stmt)
+static Oid
+ValidateRestrictionEstimator(List *restrictionName)
 {
-       List       *operatorName = stmt->name;
-       TypeName   *typeName1 = (TypeName *) linitial(stmt->args);
-       TypeName   *typeName2 = (TypeName *) lsecond(stmt->args);
-       Oid                     operOid;
-       HeapTuple       tup;
-       ObjectAddress object;
+       Oid                     typeId[4];
+       Oid                     restrictionOid;
+       AclResult       aclresult;
 
-       Assert(list_length(stmt->args) == 2);
-       operOid = LookupOperNameTypeNames(NULL, operatorName,
-                                                                         typeName1, typeName2,
-                                                                         stmt->missing_ok, -1);
+       typeId[0] = INTERNALOID;        /* PlannerInfo */
+       typeId[1] = OIDOID;                     /* operator OID */
+       typeId[2] = INTERNALOID;        /* args list */
+       typeId[3] = INT4OID;            /* varRelid */
 
-       if (stmt->missing_ok && !OidIsValid(operOid))
-       {
-               ereport(NOTICE,
-                               (errmsg("operator %s does not exist, skipping",
-                                               NameListToString(operatorName))));
-               return;
-       }
+       restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
 
-       tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for operator %u", operOid);
+       /* 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")));
 
-       /* 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, ACL_KIND_OPER,
-                                          NameListToString(operatorName));
+       /* 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));
 
-       ReleaseSysCache(tup);
+       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 = OperatorRelationId;
-       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")));
+
+       /* 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));
 
-       performDeletion(&object, stmt->behavior);
+       return joinOid;
 }
 
 /*
@@ -349,12 +341,32 @@ RemoveOperatorById(Oid operOid)
 {
        Relation        relation;
        HeapTuple       tup;
+       Form_pg_operator op;
 
        relation = heap_open(OperatorRelationId, 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);
+
+       /*
+        * 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);
 
@@ -363,92 +375,157 @@ RemoveOperatorById(Oid operOid)
        heap_close(relation, RowExclusiveLock);
 }
 
-void
-AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId)
-{
-       Relation        rel;
-
-       rel = heap_open(OperatorRelationId, RowExclusiveLock);
-
-       AlterOperatorOwner_internal(rel, operOid, newOwnerId);
-
-       heap_close(rel, NoLock);
-}
-
 /*
- * change operator owner
+ * AlterOperator
+ *             routine implementing ALTER OPERATOR <operator> SET (option = ...).
+ *
+ * Currently, only RESTRICT and JOIN estimator functions can be changed.
  */
-void
-AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
-                                  Oid newOwnerId)
-{
-       Oid                     operOid;
-       Relation        rel;
-
-       rel = heap_open(OperatorRelationId, RowExclusiveLock);
-
-       operOid = LookupOperNameTypeNames(NULL, name,
-                                                                         typeName1, typeName2,
-                                                                         false, -1);
-
-       AlterOperatorOwner_internal(rel, operOid, newOwnerId);
-
-       heap_close(rel, NoLock);
-}
-
-static void
-AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
+ObjectAddress
+AlterOperator(AlterOperatorStmt *stmt)
 {
+       ObjectAddress address;
+       Oid                     oprId;
+       Relation        catalog;
        HeapTuple       tup;
-       AclResult       aclresult;
        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;
 
-       Assert(RelationGetRelid(rel) == OperatorRelationId);
-
-       tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for operator %u", operOid);
-
+       /* 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);
 
-       /*
-        * If the new owner is the same as the existing owner, consider the
-        * command to have succeeded.  This is for dump restoration purposes.
-        */
-       if (oprForm->oprowner != newOwnerId)
+       /* Process options */
+       foreach(pl, stmt->options)
        {
-               /* Superusers can always do it */
-               if (!superuser())
+               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)
                {
-                       /* Otherwise, must be owner of the existing object */
-                       if (!pg_oper_ownercheck(operOid, GetUserId()))
-                               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
-                                                          NameStr(oprForm->oprname));
-
-                       /* Must be able to become new owner */
-                       check_is_member_of_role(GetUserId(), newOwnerId);
-
-                       /* New owner must have CREATE privilege on namespace */
-                       aclresult = pg_namespace_aclcheck(oprForm->oprnamespace,
-                                                                                         newOwnerId,
-                                                                                         ACL_CREATE);
-                       if (aclresult != ACLCHECK_OK)
-                               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
-                                                          get_namespace_name(oprForm->oprnamespace));
+                       restrictionName = param;
+                       updateRestriction = true;
+               }
+               else if (pg_strcasecmp(defel->defname, "join") == 0)
+               {
+                       joinName = param;
+                       updateJoin = true;
                }
 
                /*
-                * Modify the owner --- okay to scribble on tup because it's a copy
+                * The rest of the options that CREATE accepts cannot be changed.
+                * Check for them so that we can give a meaningful error message.
                 */
-               oprForm->oprowner = newOwnerId;
+               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;
 
-               simple_heap_update(rel, &tup->t_self, tup);
+       /* 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")));
+       }
 
-               CatalogUpdateIndexes(rel, tup);
+       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 owner dependency reference */
-               changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId);
+       /* 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);
 
-       heap_freetuple(tup);
+       return address;
 }