LCOV - differential code coverage report
Current view: top level - src/backend/rewrite - rewriteDefine.c (source / functions) Coverage Total Hit UNC UBC GNC CBC
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 86.8 % 242 210 32 4 206
Current Date: 2026-08-27 14:31:44 +0300 Functions: 100.0 % 10 10 2 8
Baseline: lcov-20260827-baseline Branches: 57.3 % 262 150 2 110 6 144
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 2 2 2
(30,360] days: 85.7 % 7 6 1 4 2
(360..) days: 86.7 % 233 202 31 202
Function coverage date bins:
(360..) days: 100.0 % 10 10 2 8
Branch coverage date bins:
(30,360] days: 64.3 % 14 9 2 3 6 3
(360..) days: 56.9 % 248 141 107 141

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

Generated by: LCOV version 2.0-1