LCOV - differential code coverage report
Current view: top level - src/backend/rewrite - rewriteDefine.c (source / functions) Coverage Total Hit UBC GBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 85.2 % 237 202 35 202
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 10 10 10
Baseline: 16@8cea358b128 Branches: 55.5 % 254 141 113 2 139
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 85.2 % 237 202 35 202
Function coverage date bins:
(240..) days: 100.0 % 10 10 10
Branch coverage date bins:
(240..) days: 55.5 % 254 141 113 2 139

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

Generated by: LCOV version 2.1-beta2-3-g6141622