LCOV - code coverage report
Current view: top level - src/backend/rewrite - rewriteDefine.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 86.7 % 240 208
Test Date: 2026-08-15 11:15:50 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 57.0 % 256 146

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

Generated by: LCOV version 2.0-1