LCOV - differential code coverage report
Current view: top level - src/backend/rewrite - rewriteDefine.c (source / functions) Coverage Total Hit UNC UBC GNC CBC
Current: 77aeca80249c9e640c811e80633a2e334a9320de vs 38afc3dcb25c45b744d4025029ce0a6c90b7059f Lines: 86.7 % 240 208 32 4 204
Current Date: 2026-07-25 19:08:27 +0900 Functions: 100.0 % 10 10 2 8
Baseline: lcov-20260725-baseline Branches: 57.3 % 262 150 2 110 6 144
Baseline Date: 2026-07-25 19:09:19 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 85.7 % 7 6 1 4 2
(30,360] days: 100.0 % 2 2 2
(360..) days: 86.6 % 231 200 31 200
Function coverage date bins:
(360..) days: 100.0 % 10 10 2 8
Branch coverage date bins:
(7,30] days: 64.3 % 14 9 2 3 6 3
(30,360] days: 50.0 % 2 1 1 1
(360..) days: 56.9 % 246 140 106 140

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

Generated by: LCOV version 2.0-1