LCOV - code coverage report
Current view: top level - src/backend/commands - operatorcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 186 204 91.2 %
Date: 2017-09-29 15:12:54 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * operatorcmds.c
       4             :  *
       5             :  *    Routines for operator manipulation commands
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/commands/operatorcmds.c
      13             :  *
      14             :  * DESCRIPTION
      15             :  *    The "DefineFoo" routines take the parse tree and pick out the
      16             :  *    appropriate arguments/flags, passing the results to the
      17             :  *    corresponding "FooDefine" routines (in src/catalog) that do
      18             :  *    the actual catalog-munging.  These routines also verify permission
      19             :  *    of the user to execute the command.
      20             :  *
      21             :  * NOTES
      22             :  *    These things must be defined and committed in the following order:
      23             :  *      "create function":
      24             :  *              input/output, recv/send procedures
      25             :  *      "create type":
      26             :  *              type
      27             :  *      "create operator":
      28             :  *              operators
      29             :  *
      30             :  *      Most of the parse-tree manipulation routines are defined in
      31             :  *      commands/manip.c.
      32             :  *
      33             :  *-------------------------------------------------------------------------
      34             :  */
      35             : #include "postgres.h"
      36             : 
      37             : #include "access/heapam.h"
      38             : #include "access/htup_details.h"
      39             : #include "catalog/dependency.h"
      40             : #include "catalog/indexing.h"
      41             : #include "catalog/objectaccess.h"
      42             : #include "catalog/pg_operator.h"
      43             : #include "catalog/pg_operator_fn.h"
      44             : #include "catalog/pg_type.h"
      45             : #include "commands/alter.h"
      46             : #include "commands/defrem.h"
      47             : #include "miscadmin.h"
      48             : #include "parser/parse_func.h"
      49             : #include "parser/parse_oper.h"
      50             : #include "parser/parse_type.h"
      51             : #include "utils/builtins.h"
      52             : #include "utils/lsyscache.h"
      53             : #include "utils/rel.h"
      54             : #include "utils/syscache.h"
      55             : 
      56             : static Oid  ValidateRestrictionEstimator(List *restrictionName);
      57             : static Oid  ValidateJoinEstimator(List *joinName);
      58             : 
      59             : /*
      60             :  * DefineOperator
      61             :  *      this function extracts all the information from the
      62             :  *      parameter list generated by the parser and then has
      63             :  *      OperatorCreate() do all the actual work.
      64             :  *
      65             :  * 'parameters' is a list of DefElem
      66             :  */
      67             : ObjectAddress
      68          38 : DefineOperator(List *names, List *parameters)
      69             : {
      70             :     char       *oprName;
      71             :     Oid         oprNamespace;
      72             :     AclResult   aclresult;
      73          38 :     bool        canMerge = false;   /* operator merges */
      74          38 :     bool        canHash = false;    /* operator hashes */
      75          38 :     List       *functionName = NIL; /* function for operator */
      76          38 :     TypeName   *typeName1 = NULL;   /* first type name */
      77          38 :     TypeName   *typeName2 = NULL;   /* second type name */
      78          38 :     Oid         typeId1 = InvalidOid;   /* types converted to OID */
      79          38 :     Oid         typeId2 = InvalidOid;
      80             :     Oid         rettype;
      81          38 :     List       *commutatorName = NIL;   /* optional commutator operator name */
      82          38 :     List       *negatorName = NIL;  /* optional negator operator name */
      83          38 :     List       *restrictionName = NIL;  /* optional restrict. sel. procedure */
      84          38 :     List       *joinName = NIL; /* optional join sel. procedure */
      85             :     Oid         functionOid;    /* functions converted to OID */
      86             :     Oid         restrictionOid;
      87             :     Oid         joinOid;
      88             :     Oid         typeId[2];      /* to hold left and right arg */
      89             :     int         nargs;
      90             :     ListCell   *pl;
      91             : 
      92             :     /* Convert list of names to a name and namespace */
      93          38 :     oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
      94             : 
      95             :     /* Check we have creation rights in target namespace */
      96          38 :     aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE);
      97          38 :     if (aclresult != ACLCHECK_OK)
      98           1 :         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
      99           1 :                        get_namespace_name(oprNamespace));
     100             : 
     101             :     /*
     102             :      * loop over the definition list and extract the information we need.
     103             :      */
     104         173 :     foreach(pl, parameters)
     105             :     {
     106         138 :         DefElem    *defel = (DefElem *) lfirst(pl);
     107             : 
     108         138 :         if (pg_strcasecmp(defel->defname, "leftarg") == 0)
     109             :         {
     110          32 :             typeName1 = defGetTypeName(defel);
     111          32 :             if (typeName1->setof)
     112           1 :                 ereport(ERROR,
     113             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     114             :                          errmsg("SETOF type not allowed for operator argument")));
     115             :         }
     116         106 :         else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
     117             :         {
     118          31 :             typeName2 = defGetTypeName(defel);
     119          31 :             if (typeName2->setof)
     120           1 :                 ereport(ERROR,
     121             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     122             :                          errmsg("SETOF type not allowed for operator argument")));
     123             :         }
     124          75 :         else if (pg_strcasecmp(defel->defname, "procedure") == 0)
     125          34 :             functionName = defGetQualifiedName(defel);
     126          41 :         else if (pg_strcasecmp(defel->defname, "commutator") == 0)
     127           9 :             commutatorName = defGetQualifiedName(defel);
     128          32 :         else if (pg_strcasecmp(defel->defname, "negator") == 0)
     129           5 :             negatorName = defGetQualifiedName(defel);
     130          27 :         else if (pg_strcasecmp(defel->defname, "restrict") == 0)
     131           8 :             restrictionName = defGetQualifiedName(defel);
     132          19 :         else if (pg_strcasecmp(defel->defname, "join") == 0)
     133           6 :             joinName = defGetQualifiedName(defel);
     134          13 :         else if (pg_strcasecmp(defel->defname, "hashes") == 0)
     135           2 :             canHash = defGetBoolean(defel);
     136          11 :         else if (pg_strcasecmp(defel->defname, "merges") == 0)
     137           6 :             canMerge = defGetBoolean(defel);
     138             :         /* These obsolete options are taken as meaning canMerge */
     139           5 :         else if (pg_strcasecmp(defel->defname, "sort1") == 0)
     140           1 :             canMerge = true;
     141           4 :         else if (pg_strcasecmp(defel->defname, "sort2") == 0)
     142           1 :             canMerge = true;
     143           3 :         else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
     144           1 :             canMerge = true;
     145           2 :         else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
     146           1 :             canMerge = true;
     147             :         else
     148             :         {
     149             :             /* WARNING, not ERROR, for historical backwards-compatibility */
     150           1 :             ereport(WARNING,
     151             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     152             :                      errmsg("operator attribute \"%s\" not recognized",
     153             :                             defel->defname)));
     154             :         }
     155             :     }
     156             : 
     157             :     /*
     158             :      * make sure we have our required definitions
     159             :      */
     160          35 :     if (functionName == NIL)
     161           1 :         ereport(ERROR,
     162             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     163             :                  errmsg("operator procedure must be specified")));
     164             : 
     165             :     /* Transform type names to type OIDs */
     166          34 :     if (typeName1)
     167          30 :         typeId1 = typenameTypeId(NULL, typeName1);
     168          34 :     if (typeName2)
     169          30 :         typeId2 = typenameTypeId(NULL, typeName2);
     170             : 
     171          34 :     if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
     172           1 :         ereport(ERROR,
     173             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     174             :                  errmsg("at least one of leftarg or rightarg must be specified")));
     175             : 
     176          33 :     if (typeName1)
     177             :     {
     178          30 :         aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE);
     179          30 :         if (aclresult != ACLCHECK_OK)
     180           2 :             aclcheck_error_type(aclresult, typeId1);
     181             :     }
     182             : 
     183          31 :     if (typeName2)
     184             :     {
     185          28 :         aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE);
     186          28 :         if (aclresult != ACLCHECK_OK)
     187           1 :             aclcheck_error_type(aclresult, typeId2);
     188             :     }
     189             : 
     190             :     /*
     191             :      * Look up the operator's underlying function.
     192             :      */
     193          30 :     if (!OidIsValid(typeId1))
     194             :     {
     195           3 :         typeId[0] = typeId2;
     196           3 :         nargs = 1;
     197             :     }
     198          27 :     else if (!OidIsValid(typeId2))
     199             :     {
     200           3 :         typeId[0] = typeId1;
     201           3 :         nargs = 1;
     202             :     }
     203             :     else
     204             :     {
     205          24 :         typeId[0] = typeId1;
     206          24 :         typeId[1] = typeId2;
     207          24 :         nargs = 2;
     208             :     }
     209          30 :     functionOid = LookupFuncName(functionName, nargs, typeId, false);
     210             : 
     211             :     /*
     212             :      * We require EXECUTE rights for the function.  This isn't strictly
     213             :      * necessary, since EXECUTE will be checked at any attempted use of the
     214             :      * operator, but it seems like a good idea anyway.
     215             :      */
     216          30 :     aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
     217          30 :     if (aclresult != ACLCHECK_OK)
     218           1 :         aclcheck_error(aclresult, ACL_KIND_PROC,
     219           1 :                        NameListToString(functionName));
     220             : 
     221          29 :     rettype = get_func_rettype(functionOid);
     222          29 :     aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
     223          29 :     if (aclresult != ACLCHECK_OK)
     224           1 :         aclcheck_error_type(aclresult, rettype);
     225             : 
     226             :     /*
     227             :      * Look up restriction and join estimators if specified
     228             :      */
     229          28 :     if (restrictionName)
     230           8 :         restrictionOid = ValidateRestrictionEstimator(restrictionName);
     231             :     else
     232          20 :         restrictionOid = InvalidOid;
     233          28 :     if (joinName)
     234           6 :         joinOid = ValidateJoinEstimator(joinName);
     235             :     else
     236          22 :         joinOid = InvalidOid;
     237             : 
     238             :     /*
     239             :      * now have OperatorCreate do all the work..
     240             :      */
     241          28 :     return
     242          28 :         OperatorCreate(oprName, /* operator name */
     243             :                        oprNamespace,    /* namespace */
     244             :                        typeId1, /* left type id */
     245             :                        typeId2, /* right type id */
     246             :                        functionOid, /* function for operator */
     247             :                        commutatorName,  /* optional commutator operator name */
     248             :                        negatorName, /* optional negator operator name */
     249             :                        restrictionOid,  /* optional restrict. sel. procedure */
     250             :                        joinOid, /* optional join sel. procedure name */
     251             :                        canMerge,    /* operator merges */
     252             :                        canHash);    /* operator hashes */
     253             : }
     254             : 
     255             : /*
     256             :  * Look up a restriction estimator function ny name, and verify that it has
     257             :  * the correct signature and we have the permissions to attach it to an
     258             :  * operator.
     259             :  */
     260             : static Oid
     261          11 : ValidateRestrictionEstimator(List *restrictionName)
     262             : {
     263             :     Oid         typeId[4];
     264             :     Oid         restrictionOid;
     265             :     AclResult   aclresult;
     266             : 
     267          11 :     typeId[0] = INTERNALOID;    /* PlannerInfo */
     268          11 :     typeId[1] = OIDOID;         /* operator OID */
     269          11 :     typeId[2] = INTERNALOID;    /* args list */
     270          11 :     typeId[3] = INT4OID;        /* varRelid */
     271             : 
     272          11 :     restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
     273             : 
     274             :     /* estimators must return float8 */
     275          10 :     if (get_func_rettype(restrictionOid) != FLOAT8OID)
     276           0 :         ereport(ERROR,
     277             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     278             :                  errmsg("restriction estimator function %s must return type %s",
     279             :                         NameListToString(restrictionName), "float8")));
     280             : 
     281             :     /* Require EXECUTE rights for the estimator */
     282          10 :     aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE);
     283          10 :     if (aclresult != ACLCHECK_OK)
     284           0 :         aclcheck_error(aclresult, ACL_KIND_PROC,
     285           0 :                        NameListToString(restrictionName));
     286             : 
     287          10 :     return restrictionOid;
     288             : }
     289             : 
     290             : /*
     291             :  * Look up a join estimator function ny name, and verify that it has the
     292             :  * correct signature and we have the permissions to attach it to an
     293             :  * operator.
     294             :  */
     295             : static Oid
     296           9 : ValidateJoinEstimator(List *joinName)
     297             : {
     298             :     Oid         typeId[5];
     299             :     Oid         joinOid;
     300             :     AclResult   aclresult;
     301             : 
     302           9 :     typeId[0] = INTERNALOID;    /* PlannerInfo */
     303           9 :     typeId[1] = OIDOID;         /* operator OID */
     304           9 :     typeId[2] = INTERNALOID;    /* args list */
     305           9 :     typeId[3] = INT2OID;        /* jointype */
     306           9 :     typeId[4] = INTERNALOID;    /* SpecialJoinInfo */
     307             : 
     308             :     /*
     309             :      * As of Postgres 8.4, the preferred signature for join estimators has 5
     310             :      * arguments, but we still allow the old 4-argument form. Try the
     311             :      * preferred form first.
     312             :      */
     313           9 :     joinOid = LookupFuncName(joinName, 5, typeId, true);
     314           9 :     if (!OidIsValid(joinOid))
     315           1 :         joinOid = LookupFuncName(joinName, 4, typeId, true);
     316             :     /* If not found, reference the 5-argument signature in error msg */
     317           9 :     if (!OidIsValid(joinOid))
     318           1 :         joinOid = LookupFuncName(joinName, 5, typeId, false);
     319             : 
     320             :     /* estimators must return float8 */
     321           8 :     if (get_func_rettype(joinOid) != FLOAT8OID)
     322           0 :         ereport(ERROR,
     323             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     324             :                  errmsg("join estimator function %s must return type %s",
     325             :                         NameListToString(joinName), "float8")));
     326             : 
     327             :     /* Require EXECUTE rights for the estimator */
     328           8 :     aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE);
     329           8 :     if (aclresult != ACLCHECK_OK)
     330           0 :         aclcheck_error(aclresult, ACL_KIND_PROC,
     331           0 :                        NameListToString(joinName));
     332             : 
     333           8 :     return joinOid;
     334             : }
     335             : 
     336             : /*
     337             :  * Guts of operator deletion.
     338             :  */
     339             : void
     340          13 : RemoveOperatorById(Oid operOid)
     341             : {
     342             :     Relation    relation;
     343             :     HeapTuple   tup;
     344             :     Form_pg_operator op;
     345             : 
     346          13 :     relation = heap_open(OperatorRelationId, RowExclusiveLock);
     347             : 
     348          13 :     tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     349          13 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     350           0 :         elog(ERROR, "cache lookup failed for operator %u", operOid);
     351          13 :     op = (Form_pg_operator) GETSTRUCT(tup);
     352             : 
     353             :     /*
     354             :      * Reset links from commutator and negator, if any.  In case of a
     355             :      * self-commutator or self-negator, this means we have to re-fetch the
     356             :      * updated tuple.  (We could optimize away updates on the tuple we're
     357             :      * about to drop, but it doesn't seem worth convoluting the logic for.)
     358             :      */
     359          13 :     if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
     360             :     {
     361           4 :         OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
     362           4 :         if (operOid == op->oprcom || operOid == op->oprnegate)
     363             :         {
     364           3 :             ReleaseSysCache(tup);
     365           3 :             tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
     366           3 :             if (!HeapTupleIsValid(tup)) /* should not happen */
     367           0 :                 elog(ERROR, "cache lookup failed for operator %u", operOid);
     368             :         }
     369             :     }
     370             : 
     371          13 :     CatalogTupleDelete(relation, &tup->t_self);
     372             : 
     373          13 :     ReleaseSysCache(tup);
     374             : 
     375          13 :     heap_close(relation, RowExclusiveLock);
     376          13 : }
     377             : 
     378             : /*
     379             :  * AlterOperator
     380             :  *      routine implementing ALTER OPERATOR <operator> SET (option = ...).
     381             :  *
     382             :  * Currently, only RESTRICT and JOIN estimator functions can be changed.
     383             :  */
     384             : ObjectAddress
     385          13 : AlterOperator(AlterOperatorStmt *stmt)
     386             : {
     387             :     ObjectAddress address;
     388             :     Oid         oprId;
     389             :     Relation    catalog;
     390             :     HeapTuple   tup;
     391             :     Form_pg_operator oprForm;
     392             :     int         i;
     393             :     ListCell   *pl;
     394             :     Datum       values[Natts_pg_operator];
     395             :     bool        nulls[Natts_pg_operator];
     396             :     bool        replaces[Natts_pg_operator];
     397          13 :     List       *restrictionName = NIL;  /* optional restrict. sel. procedure */
     398          13 :     bool        updateRestriction = false;
     399             :     Oid         restrictionOid;
     400          13 :     List       *joinName = NIL; /* optional join sel. procedure */
     401          13 :     bool        updateJoin = false;
     402             :     Oid         joinOid;
     403             : 
     404             :     /* Look up the operator */
     405          13 :     oprId = LookupOperWithArgs(stmt->opername, false);
     406          13 :     catalog = heap_open(OperatorRelationId, RowExclusiveLock);
     407          13 :     tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
     408          13 :     if (tup == NULL)
     409           0 :         elog(ERROR, "cache lookup failed for operator %u", oprId);
     410          13 :     oprForm = (Form_pg_operator) GETSTRUCT(tup);
     411             : 
     412             :     /* Process options */
     413          24 :     foreach(pl, stmt->options)
     414             :     {
     415          15 :         DefElem    *defel = (DefElem *) lfirst(pl);
     416             :         List       *param;
     417             : 
     418          15 :         if (defel->arg == NULL)
     419           5 :             param = NIL;        /* NONE, removes the function */
     420             :         else
     421          10 :             param = defGetQualifiedName(defel);
     422             : 
     423          15 :         if (pg_strcasecmp(defel->defname, "restrict") == 0)
     424             :         {
     425           6 :             restrictionName = param;
     426           6 :             updateRestriction = true;
     427             :         }
     428           9 :         else if (pg_strcasecmp(defel->defname, "join") == 0)
     429             :         {
     430           5 :             joinName = param;
     431           5 :             updateJoin = true;
     432             :         }
     433             : 
     434             :         /*
     435             :          * The rest of the options that CREATE accepts cannot be changed.
     436             :          * Check for them so that we can give a meaningful error message.
     437             :          */
     438           8 :         else if (pg_strcasecmp(defel->defname, "leftarg") == 0 ||
     439           8 :                  pg_strcasecmp(defel->defname, "rightarg") == 0 ||
     440           8 :                  pg_strcasecmp(defel->defname, "procedure") == 0 ||
     441           6 :                  pg_strcasecmp(defel->defname, "commutator") == 0 ||
     442           2 :                  pg_strcasecmp(defel->defname, "negator") == 0 ||
     443           0 :                  pg_strcasecmp(defel->defname, "hashes") == 0 ||
     444           0 :                  pg_strcasecmp(defel->defname, "merges") == 0)
     445             :         {
     446           4 :             ereport(ERROR,
     447             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     448             :                      errmsg("operator attribute \"%s\" cannot be changed",
     449             :                             defel->defname)));
     450             :         }
     451             :         else
     452           0 :             ereport(ERROR,
     453             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     454             :                      errmsg("operator attribute \"%s\" not recognized",
     455             :                             defel->defname)));
     456             :     }
     457             : 
     458             :     /* Check permissions. Must be owner. */
     459           9 :     if (!pg_oper_ownercheck(oprId, GetUserId()))
     460           1 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
     461           1 :                        NameStr(oprForm->oprname));
     462             : 
     463             :     /*
     464             :      * Look up restriction and join estimators if specified
     465             :      */
     466           8 :     if (restrictionName)
     467           3 :         restrictionOid = ValidateRestrictionEstimator(restrictionName);
     468             :     else
     469           5 :         restrictionOid = InvalidOid;
     470           7 :     if (joinName)
     471           3 :         joinOid = ValidateJoinEstimator(joinName);
     472             :     else
     473           4 :         joinOid = InvalidOid;
     474             : 
     475             :     /* Perform additional checks, like OperatorCreate does */
     476           6 :     if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright)))
     477             :     {
     478             :         /* If it's not a binary op, these things mustn't be set: */
     479           0 :         if (OidIsValid(joinOid))
     480           0 :             ereport(ERROR,
     481             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     482             :                      errmsg("only binary operators can have join selectivity")));
     483             :     }
     484             : 
     485           6 :     if (oprForm->oprresult != BOOLOID)
     486             :     {
     487           0 :         if (OidIsValid(restrictionOid))
     488           0 :             ereport(ERROR,
     489             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     490             :                      errmsg("only boolean operators can have restriction selectivity")));
     491           0 :         if (OidIsValid(joinOid))
     492           0 :             ereport(ERROR,
     493             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     494             :                      errmsg("only boolean operators can have join selectivity")));
     495             :     }
     496             : 
     497             :     /* Update the tuple */
     498          90 :     for (i = 0; i < Natts_pg_operator; ++i)
     499             :     {
     500          84 :         values[i] = (Datum) 0;
     501          84 :         replaces[i] = false;
     502          84 :         nulls[i] = false;
     503             :     }
     504           6 :     if (updateRestriction)
     505             :     {
     506           4 :         replaces[Anum_pg_operator_oprrest - 1] = true;
     507           4 :         values[Anum_pg_operator_oprrest - 1] = restrictionOid;
     508             :     }
     509           6 :     if (updateJoin)
     510             :     {
     511           4 :         replaces[Anum_pg_operator_oprjoin - 1] = true;
     512           4 :         values[Anum_pg_operator_oprjoin - 1] = joinOid;
     513             :     }
     514             : 
     515           6 :     tup = heap_modify_tuple(tup, RelationGetDescr(catalog),
     516             :                             values, nulls, replaces);
     517             : 
     518           6 :     CatalogTupleUpdate(catalog, &tup->t_self, tup);
     519             : 
     520           6 :     address = makeOperatorDependencies(tup, true);
     521             : 
     522           6 :     InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
     523             : 
     524           6 :     heap_close(catalog, NoLock);
     525             : 
     526           6 :     return address;
     527             : }

Generated by: LCOV version 1.11