LCOV - code coverage report
Current view: top level - src/backend/rewrite - rewriteDefine.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 85.9 % 306 263
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 44.4 % 311 138

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * rewriteDefine.c
       4                 :             :  *        routines for defining a rewrite rule
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/backend/rewrite/rewriteDefine.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/htup_details.h"
      18                 :             : #include "access/relation.h"
      19                 :             : #include "access/table.h"
      20                 :             : #include "catalog/catalog.h"
      21                 :             : #include "catalog/dependency.h"
      22                 :             : #include "catalog/indexing.h"
      23                 :             : #include "catalog/namespace.h"
      24                 :             : #include "catalog/objectaccess.h"
      25                 :             : #include "catalog/pg_rewrite.h"
      26                 :             : #include "miscadmin.h"
      27                 :             : #include "nodes/nodeFuncs.h"
      28                 :             : #include "parser/parse_utilcmd.h"
      29                 :             : #include "rewrite/rewriteDefine.h"
      30                 :             : #include "rewrite/rewriteManip.h"
      31                 :             : #include "rewrite/rewriteSupport.h"
      32                 :             : #include "utils/acl.h"
      33                 :             : #include "utils/builtins.h"
      34                 :             : #include "utils/inval.h"
      35                 :             : #include "utils/lsyscache.h"
      36                 :             : #include "utils/rel.h"
      37                 :             : #include "utils/syscache.h"
      38                 :             : 
      39                 :             : 
      40                 :             : static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
      41                 :             :                                                                 bool isSelect, bool requireColumnNameMatch);
      42                 :             : static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
      43                 :             : static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
      44                 :             : 
      45                 :             : 
      46                 :             : /*
      47                 :             :  * InsertRule -
      48                 :             :  *        takes the arguments and inserts them as a row into the system
      49                 :             :  *        relation "pg_rewrite"
      50                 :             :  */
      51                 :             : static Oid
      52                 :         755 : InsertRule(const char *rulname,
      53                 :             :                    int evtype,
      54                 :             :                    Oid eventrel_oid,
      55                 :             :                    bool evinstead,
      56                 :             :                    Node *event_qual,
      57                 :             :                    List *action,
      58                 :             :                    bool replace)
      59                 :             : {
      60                 :         755 :         char       *evqual = nodeToString(event_qual);
      61                 :         755 :         char       *actiontree = nodeToString((Node *) action);
      62                 :         755 :         Datum           values[Natts_pg_rewrite];
      63                 :         755 :         bool            nulls[Natts_pg_rewrite] = {0};
      64                 :         755 :         NameData        rname;
      65                 :         755 :         Relation        pg_rewrite_desc;
      66                 :         755 :         HeapTuple       tup,
      67                 :             :                                 oldtup;
      68                 :         755 :         Oid                     rewriteObjectId;
      69                 :         755 :         ObjectAddress myself,
      70                 :             :                                 referenced;
      71                 :         755 :         bool            is_update = false;
      72                 :             : 
      73                 :             :         /*
      74                 :             :          * Set up *nulls and *values arrays
      75                 :             :          */
      76                 :         755 :         namestrcpy(&rname, rulname);
      77                 :         755 :         values[Anum_pg_rewrite_rulename - 1] = NameGetDatum(&rname);
      78                 :         755 :         values[Anum_pg_rewrite_ev_class - 1] = ObjectIdGetDatum(eventrel_oid);
      79                 :         755 :         values[Anum_pg_rewrite_ev_type - 1] = CharGetDatum(evtype + '0');
      80                 :         755 :         values[Anum_pg_rewrite_ev_enabled - 1] = CharGetDatum(RULE_FIRES_ON_ORIGIN);
      81                 :         755 :         values[Anum_pg_rewrite_is_instead - 1] = BoolGetDatum(evinstead);
      82                 :         755 :         values[Anum_pg_rewrite_ev_qual - 1] = CStringGetTextDatum(evqual);
      83                 :         755 :         values[Anum_pg_rewrite_ev_action - 1] = CStringGetTextDatum(actiontree);
      84                 :             : 
      85                 :             :         /*
      86                 :             :          * Ready to store new pg_rewrite tuple
      87                 :             :          */
      88                 :         755 :         pg_rewrite_desc = table_open(RewriteRelationId, RowExclusiveLock);
      89                 :             : 
      90                 :             :         /*
      91                 :             :          * Check to see if we are replacing an existing tuple
      92                 :             :          */
      93                 :         755 :         oldtup = SearchSysCache2(RULERELNAME,
      94                 :         755 :                                                          ObjectIdGetDatum(eventrel_oid),
      95                 :         755 :                                                          PointerGetDatum(rulname));
      96                 :             : 
      97         [ +  + ]:         755 :         if (HeapTupleIsValid(oldtup))
      98                 :             :         {
      99                 :          39 :                 bool            replaces[Natts_pg_rewrite] = {0};
     100                 :             : 
     101         [ +  - ]:          39 :                 if (!replace)
     102   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     103                 :             :                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     104                 :             :                                          errmsg("rule \"%s\" for relation \"%s\" already exists",
     105                 :             :                                                         rulname, get_rel_name(eventrel_oid))));
     106                 :             : 
     107                 :             :                 /*
     108                 :             :                  * When replacing, we don't need to replace every attribute
     109                 :             :                  */
     110                 :          39 :                 replaces[Anum_pg_rewrite_ev_type - 1] = true;
     111                 :          39 :                 replaces[Anum_pg_rewrite_is_instead - 1] = true;
     112                 :          39 :                 replaces[Anum_pg_rewrite_ev_qual - 1] = true;
     113                 :          39 :                 replaces[Anum_pg_rewrite_ev_action - 1] = true;
     114                 :             : 
     115                 :          78 :                 tup = heap_modify_tuple(oldtup, RelationGetDescr(pg_rewrite_desc),
     116                 :          39 :                                                                 values, nulls, replaces);
     117                 :             : 
     118                 :          39 :                 CatalogTupleUpdate(pg_rewrite_desc, &tup->t_self, tup);
     119                 :             : 
     120                 :          39 :                 ReleaseSysCache(oldtup);
     121                 :             : 
     122                 :          39 :                 rewriteObjectId = ((Form_pg_rewrite) GETSTRUCT(tup))->oid;
     123                 :          39 :                 is_update = true;
     124                 :          39 :         }
     125                 :             :         else
     126                 :             :         {
     127                 :         716 :                 rewriteObjectId = GetNewOidWithIndex(pg_rewrite_desc,
     128                 :             :                                                                                          RewriteOidIndexId,
     129                 :             :                                                                                          Anum_pg_rewrite_oid);
     130                 :         716 :                 values[Anum_pg_rewrite_oid - 1] = ObjectIdGetDatum(rewriteObjectId);
     131                 :             : 
     132                 :         716 :                 tup = heap_form_tuple(pg_rewrite_desc->rd_att, values, nulls);
     133                 :             : 
     134                 :         716 :                 CatalogTupleInsert(pg_rewrite_desc, tup);
     135                 :             :         }
     136                 :             : 
     137                 :             : 
     138                 :         755 :         heap_freetuple(tup);
     139                 :             : 
     140                 :             :         /* If replacing, get rid of old dependencies and make new ones */
     141         [ +  + ]:         755 :         if (is_update)
     142                 :          39 :                 deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false);
     143                 :             : 
     144                 :             :         /*
     145                 :             :          * Install dependency on rule's relation to ensure it will go away on
     146                 :             :          * relation deletion.  If the rule is ON SELECT, make the dependency
     147                 :             :          * implicit --- this prevents deleting a view's SELECT rule.  Other kinds
     148                 :             :          * of rules can be AUTO.
     149                 :             :          */
     150                 :         755 :         myself.classId = RewriteRelationId;
     151                 :         755 :         myself.objectId = rewriteObjectId;
     152                 :         755 :         myself.objectSubId = 0;
     153                 :             : 
     154                 :         755 :         referenced.classId = RelationRelationId;
     155                 :         755 :         referenced.objectId = eventrel_oid;
     156                 :         755 :         referenced.objectSubId = 0;
     157                 :             : 
     158                 :         755 :         recordDependencyOn(&myself, &referenced,
     159                 :         755 :                                            (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
     160                 :             : 
     161                 :             :         /*
     162                 :             :          * Also install dependencies on objects referenced in action and qual.
     163                 :             :          */
     164                 :         755 :         recordDependencyOnExpr(&myself, (Node *) action, NIL,
     165                 :             :                                                    DEPENDENCY_NORMAL);
     166                 :             : 
     167         [ +  + ]:         755 :         if (event_qual != NULL)
     168                 :             :         {
     169                 :             :                 /* Find query containing OLD/NEW rtable entries */
     170                 :          26 :                 Query      *qry = linitial_node(Query, action);
     171                 :             : 
     172                 :          26 :                 qry = getInsertSelectQuery(qry, NULL);
     173                 :          26 :                 recordDependencyOnExpr(&myself, event_qual, qry->rtable,
     174                 :             :                                                            DEPENDENCY_NORMAL);
     175                 :          26 :         }
     176                 :             : 
     177                 :             :         /* Post creation hook for new rule */
     178         [ +  - ]:         755 :         InvokeObjectPostCreateHook(RewriteRelationId, rewriteObjectId, 0);
     179                 :             : 
     180                 :         755 :         table_close(pg_rewrite_desc, RowExclusiveLock);
     181                 :             : 
     182                 :        1510 :         return rewriteObjectId;
     183                 :         755 : }
     184                 :             : 
     185                 :             : /*
     186                 :             :  * DefineRule
     187                 :             :  *              Execute a CREATE RULE command.
     188                 :             :  */
     189                 :             : ObjectAddress
     190                 :         137 : DefineRule(RuleStmt *stmt, const char *queryString)
     191                 :             : {
     192                 :         137 :         List       *actions;
     193                 :         137 :         Node       *whereClause;
     194                 :         137 :         Oid                     relId;
     195                 :             : 
     196                 :             :         /* Parse analysis. */
     197                 :         137 :         transformRuleStmt(stmt, queryString, &actions, &whereClause);
     198                 :             : 
     199                 :             :         /*
     200                 :             :          * Find and lock the relation.  Lock level should match
     201                 :             :          * DefineQueryRewrite.
     202                 :             :          */
     203                 :         137 :         relId = RangeVarGetRelid(stmt->relation, AccessExclusiveLock, false);
     204                 :             : 
     205                 :             :         /* ... and execute */
     206                 :         274 :         return DefineQueryRewrite(stmt->rulename,
     207                 :         137 :                                                           relId,
     208                 :         137 :                                                           whereClause,
     209                 :         137 :                                                           stmt->event,
     210                 :         137 :                                                           stmt->instead,
     211                 :         137 :                                                           stmt->replace,
     212                 :         137 :                                                           actions);
     213                 :         137 : }
     214                 :             : 
     215                 :             : 
     216                 :             : /*
     217                 :             :  * DefineQueryRewrite
     218                 :             :  *              Create a rule
     219                 :             :  *
     220                 :             :  * This is essentially the same as DefineRule() except that the rule's
     221                 :             :  * action and qual have already been passed through parse analysis.
     222                 :             :  */
     223                 :             : ObjectAddress
     224                 :         760 : DefineQueryRewrite(const char *rulename,
     225                 :             :                                    Oid event_relid,
     226                 :             :                                    Node *event_qual,
     227                 :             :                                    CmdType event_type,
     228                 :             :                                    bool is_instead,
     229                 :             :                                    bool replace,
     230                 :             :                                    List *action)
     231                 :             : {
     232                 :         760 :         Relation        event_relation;
     233                 :         760 :         ListCell   *l;
     234                 :         760 :         Query      *query;
     235                 :         760 :         Oid                     ruleId = InvalidOid;
     236                 :             :         ObjectAddress address;
     237                 :             : 
     238                 :             :         /*
     239                 :             :          * If we are installing an ON SELECT rule, we had better grab
     240                 :             :          * AccessExclusiveLock to ensure no SELECTs are currently running on the
     241                 :             :          * event relation. For other types of rules, it would be sufficient to
     242                 :             :          * grab ShareRowExclusiveLock to lock out insert/update/delete actions and
     243                 :             :          * to ensure that we lock out current CREATE RULE statements; but because
     244                 :             :          * of race conditions in access to catalog entries, we can't do that yet.
     245                 :             :          *
     246                 :             :          * Note that this lock level should match the one used in DefineRule.
     247                 :             :          */
     248                 :         760 :         event_relation = table_open(event_relid, AccessExclusiveLock);
     249                 :             : 
     250                 :             :         /*
     251                 :             :          * Verify relation is of a type that rules can sensibly be applied to.
     252                 :             :          * Internal callers can target materialized views, but transformRuleStmt()
     253                 :             :          * blocks them for users.  Don't mention them in the error message.
     254                 :             :          */
     255         [ +  + ]:         760 :         if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
     256         [ +  + ]:         671 :                 event_relation->rd_rel->relkind != RELKIND_MATVIEW &&
     257   [ +  +  +  - ]:         621 :                 event_relation->rd_rel->relkind != RELKIND_VIEW &&
     258                 :           2 :                 event_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     259   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     260                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     261                 :             :                                  errmsg("relation \"%s\" cannot have rules",
     262                 :             :                                                 RelationGetRelationName(event_relation)),
     263                 :             :                                  errdetail_relkind_not_supported(event_relation->rd_rel->relkind)));
     264                 :             : 
     265   [ +  +  +  - ]:         760 :         if (!allowSystemTableMods && IsSystemRelation(event_relation))
     266   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     267                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     268                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
     269                 :             :                                                 RelationGetRelationName(event_relation))));
     270                 :             : 
     271                 :             :         /*
     272                 :             :          * Check user has permission to apply rules to this relation.
     273                 :             :          */
     274         [ +  - ]:         760 :         if (!object_ownercheck(RelationRelationId, event_relid, GetUserId()))
     275                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(event_relation->rd_rel->relkind),
     276                 :           0 :                                            RelationGetRelationName(event_relation));
     277                 :             : 
     278                 :             :         /*
     279                 :             :          * No rule actions that modify OLD or NEW
     280                 :             :          */
     281   [ +  +  +  +  :        1526 :         foreach(l, action)
                   +  + ]
     282                 :             :         {
     283                 :         766 :                 query = lfirst_node(Query, l);
     284         [ +  + ]:         766 :                 if (query->resultRelation == 0)
     285                 :         648 :                         continue;
     286                 :             :                 /* Don't be fooled by INSERT/SELECT */
     287         [ +  + ]:         118 :                 if (query != getInsertSelectQuery(query, NULL))
     288                 :           9 :                         continue;
     289         [ +  - ]:         109 :                 if (query->resultRelation == PRS2_OLD_VARNO)
     290   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     291                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     292                 :             :                                          errmsg("rule actions on OLD are not implemented"),
     293                 :             :                                          errhint("Use views or triggers instead.")));
     294         [ +  - ]:         109 :                 if (query->resultRelation == PRS2_NEW_VARNO)
     295   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     296                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     297                 :             :                                          errmsg("rule actions on NEW are not implemented"),
     298                 :             :                                          errhint("Use triggers instead.")));
     299                 :         109 :         }
     300                 :             : 
     301         [ +  + ]:         758 :         if (event_type == CMD_SELECT)
     302                 :             :         {
     303                 :             :                 /*
     304                 :             :                  * Rules ON SELECT are restricted to view definitions
     305                 :             :                  *
     306                 :             :                  * So this had better be a view, ...
     307                 :             :                  */
     308   [ +  +  +  + ]:         628 :                 if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
     309                 :          53 :                         event_relation->rd_rel->relkind != RELKIND_MATVIEW)
     310   [ +  -  +  - ]:           3 :                         ereport(ERROR,
     311                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     312                 :             :                                          errmsg("relation \"%s\" cannot have ON SELECT rules",
     313                 :             :                                                         RelationGetRelationName(event_relation)),
     314                 :             :                                          errdetail_relkind_not_supported(event_relation->rd_rel->relkind)));
     315                 :             : 
     316                 :             :                 /*
     317                 :             :                  * ... there cannot be INSTEAD NOTHING, ...
     318                 :             :                  */
     319         [ +  - ]:         625 :                 if (action == NIL)
     320   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     321                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     322                 :             :                                          errmsg("INSTEAD NOTHING rules on SELECT are not implemented"),
     323                 :             :                                          errhint("Use views instead.")));
     324                 :             : 
     325                 :             :                 /*
     326                 :             :                  * ... there cannot be multiple actions, ...
     327                 :             :                  */
     328         [ +  - ]:         625 :                 if (list_length(action) > 1)
     329   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     330                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     331                 :             :                                          errmsg("multiple actions for rules on SELECT are not implemented")));
     332                 :             : 
     333                 :             :                 /*
     334                 :             :                  * ... the one action must be a SELECT, ...
     335                 :             :                  */
     336                 :         625 :                 query = linitial_node(Query, action);
     337         [ +  - ]:         625 :                 if (!is_instead ||
     338                 :         625 :                         query->commandType != CMD_SELECT)
     339   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     340                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     341                 :             :                                          errmsg("rules on SELECT must have action INSTEAD SELECT")));
     342                 :             : 
     343                 :             :                 /*
     344                 :             :                  * ... it cannot contain data-modifying WITH ...
     345                 :             :                  */
     346         [ +  - ]:         625 :                 if (query->hasModifyingCTE)
     347   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     348                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     349                 :             :                                          errmsg("rules on SELECT must not contain data-modifying statements in WITH")));
     350                 :             : 
     351                 :             :                 /*
     352                 :             :                  * ... there can be no rule qual, ...
     353                 :             :                  */
     354         [ +  - ]:         625 :                 if (event_qual != NULL)
     355   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     356                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     357                 :             :                                          errmsg("event qualifications are not implemented for rules on SELECT")));
     358                 :             : 
     359                 :             :                 /*
     360                 :             :                  * ... the targetlist of the SELECT action must exactly match the
     361                 :             :                  * event relation, ...
     362                 :             :                  */
     363                 :        1250 :                 checkRuleResultList(query->targetList,
     364                 :         625 :                                                         RelationGetDescr(event_relation),
     365                 :             :                                                         true,
     366                 :         625 :                                                         event_relation->rd_rel->relkind !=
     367                 :             :                                                         RELKIND_MATVIEW);
     368                 :             : 
     369                 :             :                 /*
     370                 :             :                  * ... there must not be another ON SELECT rule already ...
     371                 :             :                  */
     372   [ +  +  +  - ]:         625 :                 if (!replace && event_relation->rd_rules != NULL)
     373                 :             :                 {
     374                 :           0 :                         int                     i;
     375                 :             : 
     376         [ #  # ]:           0 :                         for (i = 0; i < event_relation->rd_rules->numLocks; i++)
     377                 :             :                         {
     378                 :           0 :                                 RewriteRule *rule;
     379                 :             : 
     380                 :           0 :                                 rule = event_relation->rd_rules->rules[i];
     381         [ #  # ]:           0 :                                 if (rule->event == CMD_SELECT)
     382   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
     383                 :             :                                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     384                 :             :                                                          errmsg("\"%s\" is already a view",
     385                 :             :                                                                         RelationGetRelationName(event_relation))));
     386                 :           0 :                         }
     387                 :           0 :                 }
     388                 :             : 
     389                 :             :                 /*
     390                 :             :                  * ... and finally the rule must be named _RETURN.
     391                 :             :                  */
     392         [ +  - ]:         625 :                 if (strcmp(rulename, ViewSelectRuleName) != 0)
     393                 :             :                 {
     394                 :             :                         /*
     395                 :             :                          * In versions before 7.3, the expected name was _RETviewname. For
     396                 :             :                          * backwards compatibility with old pg_dump output, accept that
     397                 :             :                          * and silently change it to _RETURN.  Since this is just a quick
     398                 :             :                          * backwards-compatibility hack, limit the number of characters
     399                 :             :                          * checked to a few less than NAMEDATALEN; this saves having to
     400                 :             :                          * worry about where a multibyte character might have gotten
     401                 :             :                          * truncated.
     402                 :             :                          */
     403         [ #  # ]:           0 :                         if (strncmp(rulename, "_RET", 4) != 0 ||
     404                 :           0 :                                 strncmp(rulename + 4, RelationGetRelationName(event_relation),
     405                 :           0 :                                                 NAMEDATALEN - 4 - 4) != 0)
     406   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     407                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     408                 :             :                                                  errmsg("view rule for \"%s\" must be named \"%s\"",
     409                 :             :                                                                 RelationGetRelationName(event_relation),
     410                 :             :                                                                 ViewSelectRuleName)));
     411                 :           0 :                         rulename = pstrdup(ViewSelectRuleName);
     412                 :           0 :                 }
     413                 :         625 :         }
     414                 :             :         else
     415                 :             :         {
     416                 :             :                 /*
     417                 :             :                  * For non-SELECT rules, a RETURNING list can appear in at most one of
     418                 :             :                  * the actions ... and there can't be any RETURNING list at all in a
     419                 :             :                  * conditional or non-INSTEAD rule.  (Actually, there can be at most
     420                 :             :                  * one RETURNING list across all rules on the same event, but it seems
     421                 :             :                  * best to enforce that at rule expansion time.)  If there is a
     422                 :             :                  * RETURNING list, it must match the event relation.
     423                 :             :                  */
     424                 :         130 :                 bool            haveReturning = false;
     425                 :             : 
     426   [ +  -  +  +  :         268 :                 foreach(l, action)
                   +  + ]
     427                 :             :                 {
     428                 :         138 :                         query = lfirst_node(Query, l);
     429                 :             : 
     430         [ +  + ]:         138 :                         if (!query->returningList)
     431                 :         117 :                                 continue;
     432         [ +  - ]:          21 :                         if (haveReturning)
     433   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     434                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     435                 :             :                                                  errmsg("cannot have multiple RETURNING lists in a rule")));
     436                 :          21 :                         haveReturning = true;
     437         [ +  - ]:          21 :                         if (event_qual != NULL)
     438   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     439                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     440                 :             :                                                  errmsg("RETURNING lists are not supported in conditional rules")));
     441         [ +  - ]:          21 :                         if (!is_instead)
     442   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     443                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     444                 :             :                                                  errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
     445                 :          42 :                         checkRuleResultList(query->returningList,
     446                 :          21 :                                                                 RelationGetDescr(event_relation),
     447                 :             :                                                                 false, false);
     448                 :          21 :                 }
     449                 :             : 
     450                 :             :                 /*
     451                 :             :                  * And finally, if it's not an ON SELECT rule then it must *not* be
     452                 :             :                  * named _RETURN.  This prevents accidentally or maliciously replacing
     453                 :             :                  * a view's ON SELECT rule with some other kind of rule.
     454                 :             :                  */
     455         [ +  - ]:         130 :                 if (strcmp(rulename, ViewSelectRuleName) == 0)
     456   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     457                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     458                 :             :                                          errmsg("non-view rule for \"%s\" must not be named \"%s\"",
     459                 :             :                                                         RelationGetRelationName(event_relation),
     460                 :             :                                                         ViewSelectRuleName)));
     461                 :         130 :         }
     462                 :             : 
     463                 :             :         /*
     464                 :             :          * This rule is allowed - prepare to install it.
     465                 :             :          */
     466                 :             : 
     467                 :             :         /* discard rule if it's null action and not INSTEAD; it's a no-op */
     468   [ -  +  #  # ]:         755 :         if (action != NIL || is_instead)
     469                 :             :         {
     470                 :        1510 :                 ruleId = InsertRule(rulename,
     471                 :         755 :                                                         event_type,
     472                 :         755 :                                                         event_relid,
     473                 :         755 :                                                         is_instead,
     474                 :         755 :                                                         event_qual,
     475                 :         755 :                                                         action,
     476                 :         755 :                                                         replace);
     477                 :             : 
     478                 :             :                 /*
     479                 :             :                  * Set pg_class 'relhasrules' field true for event relation.
     480                 :             :                  *
     481                 :             :                  * Important side effect: an SI notice is broadcast to force all
     482                 :             :                  * backends (including me!) to update relcache entries with the new
     483                 :             :                  * rule.
     484                 :             :                  */
     485                 :         755 :                 SetRelationRuleStatus(event_relid, true);
     486                 :         755 :         }
     487                 :             : 
     488                 :         755 :         ObjectAddressSet(address, RewriteRelationId, ruleId);
     489                 :             : 
     490                 :             :         /* Close rel, but keep lock till commit... */
     491                 :         755 :         table_close(event_relation, NoLock);
     492                 :             : 
     493                 :             :         return address;
     494                 :         755 : }
     495                 :             : 
     496                 :             : /*
     497                 :             :  * checkRuleResultList
     498                 :             :  *              Verify that targetList produces output compatible with a tupledesc
     499                 :             :  *
     500                 :             :  * The targetList might be either a SELECT targetlist, or a RETURNING list;
     501                 :             :  * isSelect tells which.  This is used for choosing error messages.
     502                 :             :  *
     503                 :             :  * A SELECT targetlist may optionally require that column names match.
     504                 :             :  */
     505                 :             : static void
     506                 :         646 : checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect,
     507                 :             :                                         bool requireColumnNameMatch)
     508                 :             : {
     509                 :         646 :         ListCell   *tllist;
     510                 :         646 :         int                     i;
     511                 :             : 
     512                 :             :         /* Only a SELECT may require a column name match. */
     513   [ +  +  +  - ]:         646 :         Assert(isSelect || !requireColumnNameMatch);
     514                 :             : 
     515                 :         646 :         i = 0;
     516   [ +  -  +  +  :        3542 :         foreach(tllist, targetList)
                   +  + ]
     517                 :             :         {
     518                 :        2897 :                 TargetEntry *tle = (TargetEntry *) lfirst(tllist);
     519                 :        2897 :                 Oid                     tletypid;
     520                 :        2897 :                 int32           tletypmod;
     521                 :        2897 :                 Form_pg_attribute attr;
     522                 :        2897 :                 char       *attname;
     523                 :             : 
     524                 :             :                 /* resjunk entries may be ignored */
     525         [ +  + ]:        2897 :                 if (tle->resjunk)
     526                 :          16 :                         continue;
     527                 :        2881 :                 i++;
     528         [ +  + ]:        2881 :                 if (i > resultDesc->natts)
     529   [ +  -  +  -  :           1 :                         ereport(ERROR,
                   -  + ]
     530                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     531                 :             :                                          isSelect ?
     532                 :             :                                          errmsg("SELECT rule's target list has too many entries") :
     533                 :             :                                          errmsg("RETURNING list has too many entries")));
     534                 :             : 
     535                 :        2880 :                 attr = TupleDescAttr(resultDesc, i - 1);
     536                 :        2880 :                 attname = NameStr(attr->attname);
     537                 :             : 
     538                 :             :                 /*
     539                 :             :                  * Disallow dropped columns in the relation.  This is not really
     540                 :             :                  * expected to happen when creating an ON SELECT rule.  It'd be
     541                 :             :                  * possible if someone tried to convert a relation with dropped
     542                 :             :                  * columns to a view, but the only case we care about supporting
     543                 :             :                  * table-to-view conversion for is pg_dump, and pg_dump won't do that.
     544                 :             :                  *
     545                 :             :                  * Unfortunately, the situation is also possible when adding a rule
     546                 :             :                  * with RETURNING to a regular table, and rejecting that case is
     547                 :             :                  * altogether more annoying.  In principle we could support it by
     548                 :             :                  * modifying the targetlist to include dummy NULL columns
     549                 :             :                  * corresponding to the dropped columns in the tupdesc.  However,
     550                 :             :                  * places like ruleutils.c would have to be fixed to not process such
     551                 :             :                  * entries, and that would take an uncertain and possibly rather large
     552                 :             :                  * amount of work.  (Note we could not dodge that by marking the dummy
     553                 :             :                  * columns resjunk, since it's precisely the non-resjunk tlist columns
     554                 :             :                  * that are expected to correspond to table columns.)
     555                 :             :                  */
     556         [ +  - ]:        2880 :                 if (attr->attisdropped)
     557   [ #  #  #  #  :           0 :                         ereport(ERROR,
                   #  # ]
     558                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     559                 :             :                                          isSelect ?
     560                 :             :                                          errmsg("cannot convert relation containing dropped columns to view") :
     561                 :             :                                          errmsg("cannot create a RETURNING list for a relation containing dropped columns")));
     562                 :             : 
     563                 :             :                 /* Check name match if required; no need for two error texts here */
     564   [ +  +  +  - ]:        2880 :                 if (requireColumnNameMatch && strcmp(tle->resname, attname) != 0)
     565   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     566                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     567                 :             :                                          errmsg("SELECT rule's target entry %d has different column name from column \"%s\"",
     568                 :             :                                                         i, attname),
     569                 :             :                                          errdetail("SELECT target entry is named \"%s\".",
     570                 :             :                                                            tle->resname)));
     571                 :             : 
     572                 :             :                 /* Check type match. */
     573                 :        2880 :                 tletypid = exprType((Node *) tle->expr);
     574         [ +  - ]:        2880 :                 if (attr->atttypid != tletypid)
     575   [ #  #  #  #  :           0 :                         ereport(ERROR,
             #  #  #  # ]
     576                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     577                 :             :                                          isSelect ?
     578                 :             :                                          errmsg("SELECT rule's target entry %d has different type from column \"%s\"",
     579                 :             :                                                         i, attname) :
     580                 :             :                                          errmsg("RETURNING list's entry %d has different type from column \"%s\"",
     581                 :             :                                                         i, attname),
     582                 :             :                                          isSelect ?
     583                 :             :                                          errdetail("SELECT target entry has type %s, but column has type %s.",
     584                 :             :                                                            format_type_be(tletypid),
     585                 :             :                                                            format_type_be(attr->atttypid)) :
     586                 :             :                                          errdetail("RETURNING list entry has type %s, but column has type %s.",
     587                 :             :                                                            format_type_be(tletypid),
     588                 :             :                                                            format_type_be(attr->atttypid))));
     589                 :             : 
     590                 :             :                 /*
     591                 :             :                  * Allow typmods to be different only if one of them is -1, ie,
     592                 :             :                  * "unspecified".  This is necessary for cases like "numeric", where
     593                 :             :                  * the table will have a filled-in default length but the select
     594                 :             :                  * rule's expression will probably have typmod = -1.
     595                 :             :                  */
     596                 :        2880 :                 tletypmod = exprTypmod((Node *) tle->expr);
     597         [ -  + ]:        2880 :                 if (attr->atttypmod != tletypmod &&
     598   [ #  #  #  # ]:           0 :                         attr->atttypmod != -1 && tletypmod != -1)
     599   [ #  #  #  #  :           0 :                         ereport(ERROR,
             #  #  #  # ]
     600                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     601                 :             :                                          isSelect ?
     602                 :             :                                          errmsg("SELECT rule's target entry %d has different size from column \"%s\"",
     603                 :             :                                                         i, attname) :
     604                 :             :                                          errmsg("RETURNING list's entry %d has different size from column \"%s\"",
     605                 :             :                                                         i, attname),
     606                 :             :                                          isSelect ?
     607                 :             :                                          errdetail("SELECT target entry has type %s, but column has type %s.",
     608                 :             :                                                            format_type_with_typemod(tletypid, tletypmod),
     609                 :             :                                                            format_type_with_typemod(attr->atttypid,
     610                 :             :                                                                                                                 attr->atttypmod)) :
     611                 :             :                                          errdetail("RETURNING list entry has type %s, but column has type %s.",
     612                 :             :                                                            format_type_with_typemod(tletypid, tletypmod),
     613                 :             :                                                            format_type_with_typemod(attr->atttypid,
     614                 :             :                                                                                                                 attr->atttypmod))));
     615      [ -  +  + ]:        2896 :         }
     616                 :             : 
     617         [ +  - ]:         645 :         if (i != resultDesc->natts)
     618   [ #  #  #  #  :           0 :                 ereport(ERROR,
                   #  # ]
     619                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     620                 :             :                                  isSelect ?
     621                 :             :                                  errmsg("SELECT rule's target list has too few entries") :
     622                 :             :                                  errmsg("RETURNING list has too few entries")));
     623                 :         645 : }
     624                 :             : 
     625                 :             : /*
     626                 :             :  * setRuleCheckAsUser
     627                 :             :  *              Recursively scan a query or expression tree and set the checkAsUser
     628                 :             :  *              field to the given userid in all RTEPermissionInfos of the query.
     629                 :             :  */
     630                 :             : void
     631                 :        5440 : setRuleCheckAsUser(Node *node, Oid userid)
     632                 :             : {
     633                 :        5440 :         (void) setRuleCheckAsUser_walker(node, &userid);
     634                 :        5440 : }
     635                 :             : 
     636                 :             : static bool
     637                 :       22213 : setRuleCheckAsUser_walker(Node *node, Oid *context)
     638                 :             : {
     639         [ +  + ]:       22213 :         if (node == NULL)
     640                 :        4908 :                 return false;
     641         [ +  + ]:       17305 :         if (IsA(node, Query))
     642                 :             :         {
     643                 :        2607 :                 setRuleCheckAsUser_Query((Query *) node, *context);
     644                 :        2607 :                 return false;
     645                 :             :         }
     646                 :       14698 :         return expression_tree_walker(node, setRuleCheckAsUser_walker,
     647                 :             :                                                                   context);
     648                 :       22213 : }
     649                 :             : 
     650                 :             : static void
     651                 :        3035 : setRuleCheckAsUser_Query(Query *qry, Oid userid)
     652                 :             : {
     653                 :        3035 :         ListCell   *l;
     654                 :             : 
     655                 :             :         /* Set in all RTEPermissionInfos for this query. */
     656   [ +  +  +  +  :        6654 :         foreach(l, qry->rteperminfos)
                   +  + ]
     657                 :             :         {
     658                 :        3619 :                 RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l);
     659                 :             : 
     660                 :        3619 :                 perminfo->checkAsUser = userid;
     661                 :        3619 :         }
     662                 :             : 
     663                 :             :         /* Now recurse to any subquery RTEs */
     664   [ +  +  +  +  :        8259 :         foreach(l, qry->rtable)
                   +  + ]
     665                 :             :         {
     666                 :        5224 :                 RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
     667                 :             : 
     668         [ +  + ]:        5224 :                 if (rte->rtekind == RTE_SUBQUERY)
     669                 :         399 :                         setRuleCheckAsUser_Query(rte->subquery, userid);
     670                 :        5224 :         }
     671                 :             : 
     672                 :             :         /* Recurse into subquery-in-WITH */
     673   [ +  +  +  +  :        3064 :         foreach(l, qry->cteList)
                   +  + ]
     674                 :             :         {
     675                 :          29 :                 CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
     676                 :             : 
     677                 :          29 :                 setRuleCheckAsUser_Query(castNode(Query, cte->ctequery), userid);
     678                 :          29 :         }
     679                 :             : 
     680                 :             :         /* If there are sublinks, search for them and process their RTEs */
     681         [ +  + ]:        3035 :         if (qry->hasSubLinks)
     682                 :         144 :                 query_tree_walker(qry, setRuleCheckAsUser_walker, &userid,
     683                 :             :                                                   QTW_IGNORE_RC_SUBQUERIES);
     684                 :        3035 : }
     685                 :             : 
     686                 :             : 
     687                 :             : /*
     688                 :             :  * Change the firing semantics of an existing rule.
     689                 :             :  */
     690                 :             : void
     691                 :           6 : EnableDisableRule(Relation rel, const char *rulename,
     692                 :             :                                   char fires_when)
     693                 :             : {
     694                 :           6 :         Relation        pg_rewrite_desc;
     695                 :           6 :         Oid                     owningRel = RelationGetRelid(rel);
     696                 :           6 :         Oid                     eventRelationOid;
     697                 :           6 :         HeapTuple       ruletup;
     698                 :           6 :         Form_pg_rewrite ruleform;
     699                 :           6 :         bool            changed = false;
     700                 :             : 
     701                 :             :         /*
     702                 :             :          * Find the rule tuple to change.
     703                 :             :          */
     704                 :           6 :         pg_rewrite_desc = table_open(RewriteRelationId, RowExclusiveLock);
     705                 :           6 :         ruletup = SearchSysCacheCopy2(RULERELNAME,
     706                 :             :                                                                   ObjectIdGetDatum(owningRel),
     707                 :             :                                                                   PointerGetDatum(rulename));
     708         [ +  - ]:           6 :         if (!HeapTupleIsValid(ruletup))
     709   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     710                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     711                 :             :                                  errmsg("rule \"%s\" for relation \"%s\" does not exist",
     712                 :             :                                                 rulename, get_rel_name(owningRel))));
     713                 :             : 
     714                 :           6 :         ruleform = (Form_pg_rewrite) GETSTRUCT(ruletup);
     715                 :             : 
     716                 :             :         /*
     717                 :             :          * Verify that the user has appropriate permissions.
     718                 :             :          */
     719                 :           6 :         eventRelationOid = ruleform->ev_class;
     720         [ +  - ]:           6 :         Assert(eventRelationOid == owningRel);
     721         [ +  - ]:           6 :         if (!object_ownercheck(RelationRelationId, eventRelationOid, GetUserId()))
     722                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(eventRelationOid)),
     723                 :           0 :                                            get_rel_name(eventRelationOid));
     724                 :             : 
     725                 :             :         /*
     726                 :             :          * Change ev_enabled if it is different from the desired new state.
     727                 :             :          */
     728         [ -  + ]:           6 :         if (ruleform->ev_enabled != fires_when)
     729                 :             :         {
     730                 :           6 :                 ruleform->ev_enabled = fires_when;
     731                 :           6 :                 CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup);
     732                 :             : 
     733                 :           6 :                 changed = true;
     734                 :           6 :         }
     735                 :             : 
     736         [ +  - ]:           6 :         InvokeObjectPostAlterHook(RewriteRelationId, ruleform->oid, 0);
     737                 :             : 
     738                 :           6 :         heap_freetuple(ruletup);
     739                 :           6 :         table_close(pg_rewrite_desc, RowExclusiveLock);
     740                 :             : 
     741                 :             :         /*
     742                 :             :          * If we changed anything, broadcast a SI inval message to force each
     743                 :             :          * backend (including our own!) to rebuild relation's relcache entry.
     744                 :             :          * Otherwise they will fail to apply the change promptly.
     745                 :             :          */
     746         [ -  + ]:           6 :         if (changed)
     747                 :           6 :                 CacheInvalidateRelcache(rel);
     748                 :           6 : }
     749                 :             : 
     750                 :             : 
     751                 :             : /*
     752                 :             :  * Perform permissions and integrity checks before acquiring a relation lock.
     753                 :             :  */
     754                 :             : static void
     755                 :           5 : RangeVarCallbackForRenameRule(const RangeVar *rv, Oid relid, Oid oldrelid,
     756                 :             :                                                           void *arg)
     757                 :             : {
     758                 :           5 :         HeapTuple       tuple;
     759                 :           5 :         Form_pg_class form;
     760                 :             : 
     761                 :           5 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
     762         [ +  - ]:           5 :         if (!HeapTupleIsValid(tuple))
     763                 :           0 :                 return;                                 /* concurrently dropped */
     764                 :           5 :         form = (Form_pg_class) GETSTRUCT(tuple);
     765                 :             : 
     766                 :             :         /* only tables and views can have rules */
     767         [ +  - ]:           5 :         if (form->relkind != RELKIND_RELATION &&
     768   [ +  +  +  - ]:           5 :                 form->relkind != RELKIND_VIEW &&
     769                 :           1 :                 form->relkind != RELKIND_PARTITIONED_TABLE)
     770   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     771                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     772                 :             :                                  errmsg("relation \"%s\" cannot have rules", rv->relname),
     773                 :             :                                  errdetail_relkind_not_supported(form->relkind)));
     774                 :             : 
     775   [ +  -  +  - ]:           5 :         if (!allowSystemTableMods && IsSystemClass(relid, form))
     776   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     777                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     778                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
     779                 :             :                                                 rv->relname)));
     780                 :             : 
     781                 :             :         /* you must own the table to rename one of its rules */
     782         [ +  - ]:           5 :         if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
     783                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
     784                 :             : 
     785                 :           5 :         ReleaseSysCache(tuple);
     786         [ -  + ]:           5 : }
     787                 :             : 
     788                 :             : /*
     789                 :             :  * Rename an existing rewrite rule.
     790                 :             :  */
     791                 :             : ObjectAddress
     792                 :           5 : RenameRewriteRule(RangeVar *relation, const char *oldName,
     793                 :             :                                   const char *newName)
     794                 :             : {
     795                 :           5 :         Oid                     relid;
     796                 :           5 :         Relation        targetrel;
     797                 :           5 :         Relation        pg_rewrite_desc;
     798                 :           5 :         HeapTuple       ruletup;
     799                 :           5 :         Form_pg_rewrite ruleform;
     800                 :           5 :         Oid                     ruleOid;
     801                 :             :         ObjectAddress address;
     802                 :             : 
     803                 :             :         /*
     804                 :             :          * Look up name, check permissions, and acquire lock (which we will NOT
     805                 :             :          * release until end of transaction).
     806                 :             :          */
     807                 :           5 :         relid = RangeVarGetRelidExtended(relation, AccessExclusiveLock,
     808                 :             :                                                                          0,
     809                 :             :                                                                          RangeVarCallbackForRenameRule,
     810                 :             :                                                                          NULL);
     811                 :             : 
     812                 :             :         /* Have lock already, so just need to build relcache entry. */
     813                 :           5 :         targetrel = relation_open(relid, NoLock);
     814                 :             : 
     815                 :             :         /* Prepare to modify pg_rewrite */
     816                 :           5 :         pg_rewrite_desc = table_open(RewriteRelationId, RowExclusiveLock);
     817                 :             : 
     818                 :             :         /* Fetch the rule's entry (it had better exist) */
     819                 :           5 :         ruletup = SearchSysCacheCopy2(RULERELNAME,
     820                 :             :                                                                   ObjectIdGetDatum(relid),
     821                 :             :                                                                   PointerGetDatum(oldName));
     822         [ +  + ]:           5 :         if (!HeapTupleIsValid(ruletup))
     823   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     824                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     825                 :             :                                  errmsg("rule \"%s\" for relation \"%s\" does not exist",
     826                 :             :                                                 oldName, RelationGetRelationName(targetrel))));
     827                 :           4 :         ruleform = (Form_pg_rewrite) GETSTRUCT(ruletup);
     828                 :           4 :         ruleOid = ruleform->oid;
     829                 :             : 
     830                 :             :         /* rule with the new name should not already exist */
     831         [ +  + ]:           4 :         if (IsDefinedRewriteRule(relid, newName))
     832   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     833                 :             :                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     834                 :             :                                  errmsg("rule \"%s\" for relation \"%s\" already exists",
     835                 :             :                                                 newName, RelationGetRelationName(targetrel))));
     836                 :             : 
     837                 :             :         /*
     838                 :             :          * We disallow renaming ON SELECT rules, because they should always be
     839                 :             :          * named "_RETURN".
     840                 :             :          */
     841         [ +  + ]:           3 :         if (ruleform->ev_type == CMD_SELECT + '0')
     842   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     843                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     844                 :             :                                  errmsg("renaming an ON SELECT rule is not allowed")));
     845                 :             : 
     846                 :             :         /* OK, do the update */
     847                 :           2 :         namestrcpy(&(ruleform->rulename), newName);
     848                 :             : 
     849                 :           2 :         CatalogTupleUpdate(pg_rewrite_desc, &ruletup->t_self, ruletup);
     850                 :             : 
     851         [ +  - ]:           2 :         InvokeObjectPostAlterHook(RewriteRelationId, ruleOid, 0);
     852                 :             : 
     853                 :           2 :         heap_freetuple(ruletup);
     854                 :           2 :         table_close(pg_rewrite_desc, RowExclusiveLock);
     855                 :             : 
     856                 :             :         /*
     857                 :             :          * Invalidate relation's relcache entry so that other backends (and this
     858                 :             :          * one too!) are sent SI message to make them rebuild relcache entries.
     859                 :             :          * (Ideally this should happen automatically...)
     860                 :             :          */
     861                 :           2 :         CacheInvalidateRelcache(targetrel);
     862                 :             : 
     863                 :           2 :         ObjectAddressSet(address, RewriteRelationId, ruleOid);
     864                 :             : 
     865                 :             :         /*
     866                 :             :          * Close rel, but keep exclusive lock!
     867                 :             :          */
     868                 :           2 :         relation_close(targetrel, NoLock);
     869                 :             : 
     870                 :             :         return address;
     871                 :           2 : }
        

Generated by: LCOV version 2.3.2-1