]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteDefine.c
Implement SQL-standard WITH clauses, including WITH RECURSIVE.
[postgresql] / src / backend / rewrite / rewriteDefine.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteDefine.c
4  *        routines for defining a rewrite rule
5  *
6  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.130 2008/10/04 21:56:54 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/namespace.h"
21 #include "catalog/pg_rewrite.h"
22 #include "miscadmin.h"
23 #include "nodes/nodeFuncs.h"
24 #include "parser/parse_utilcmd.h"
25 #include "rewrite/rewriteDefine.h"
26 #include "rewrite/rewriteManip.h"
27 #include "rewrite/rewriteSupport.h"
28 #include "storage/smgr.h"
29 #include "utils/acl.h"
30 #include "utils/builtins.h"
31 #include "utils/inval.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
35 #include "utils/tqual.h"
36
37
38 static void checkRuleResultList(List *targetList, TupleDesc resultDesc,
39                                         bool isSelect);
40 static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
41 static void setRuleCheckAsUser_Query(Query *qry, Oid userid);
42
43
44 /*
45  * InsertRule -
46  *        takes the arguments and inserts them as a row into the system
47  *        relation "pg_rewrite"
48  */
49 static Oid
50 InsertRule(char *rulname,
51                    int evtype,
52                    Oid eventrel_oid,
53                    AttrNumber evslot_index,
54                    bool evinstead,
55                    Node *event_qual,
56                    List *action,
57                    bool replace)
58 {
59         char       *evqual = nodeToString(event_qual);
60         char       *actiontree = nodeToString((Node *) action);
61         int                     i;
62         Datum           values[Natts_pg_rewrite];
63         char            nulls[Natts_pg_rewrite];
64         char            replaces[Natts_pg_rewrite];
65         NameData        rname;
66         Relation        pg_rewrite_desc;
67         HeapTuple       tup,
68                                 oldtup;
69         Oid                     rewriteObjectId;
70         ObjectAddress myself,
71                                 referenced;
72         bool            is_update = false;
73
74         /*
75          * Set up *nulls and *values arrays
76          */
77         MemSet(nulls, ' ', sizeof(nulls));
78
79         i = 0;
80         namestrcpy(&rname, rulname);
81         values[i++] = NameGetDatum(&rname); /* rulename */
82         values[i++] = ObjectIdGetDatum(eventrel_oid);           /* ev_class */
83         values[i++] = Int16GetDatum(evslot_index);      /* ev_attr */
84         values[i++] = CharGetDatum(evtype + '0');       /* ev_type */
85         values[i++] = CharGetDatum(RULE_FIRES_ON_ORIGIN);       /* ev_enabled */
86         values[i++] = BoolGetDatum(evinstead);          /* is_instead */
87         values[i++] = CStringGetTextDatum(evqual);      /* ev_qual */
88         values[i++] = CStringGetTextDatum(actiontree);          /* ev_action */
89
90         /*
91          * Ready to store new pg_rewrite tuple
92          */
93         pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
94
95         /*
96          * Check to see if we are replacing an existing tuple
97          */
98         oldtup = SearchSysCache(RULERELNAME,
99                                                         ObjectIdGetDatum(eventrel_oid),
100                                                         PointerGetDatum(rulname),
101                                                         0, 0);
102
103         if (HeapTupleIsValid(oldtup))
104         {
105                 if (!replace)
106                         ereport(ERROR,
107                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
108                                          errmsg("rule \"%s\" for relation \"%s\" already exists",
109                                                         rulname, get_rel_name(eventrel_oid))));
110
111                 /*
112                  * When replacing, we don't need to replace every attribute
113                  */
114                 MemSet(replaces, ' ', sizeof(replaces));
115                 replaces[Anum_pg_rewrite_ev_attr - 1] = 'r';
116                 replaces[Anum_pg_rewrite_ev_type - 1] = 'r';
117                 replaces[Anum_pg_rewrite_is_instead - 1] = 'r';
118                 replaces[Anum_pg_rewrite_ev_qual - 1] = 'r';
119                 replaces[Anum_pg_rewrite_ev_action - 1] = 'r';
120
121                 tup = heap_modifytuple(oldtup, RelationGetDescr(pg_rewrite_desc),
122                                                            values, nulls, replaces);
123
124                 simple_heap_update(pg_rewrite_desc, &tup->t_self, tup);
125
126                 ReleaseSysCache(oldtup);
127
128                 rewriteObjectId = HeapTupleGetOid(tup);
129                 is_update = true;
130         }
131         else
132         {
133                 tup = heap_formtuple(pg_rewrite_desc->rd_att, values, nulls);
134
135                 rewriteObjectId = simple_heap_insert(pg_rewrite_desc, tup);
136         }
137
138         /* Need to update indexes in either case */
139         CatalogUpdateIndexes(pg_rewrite_desc, tup);
140
141         heap_freetuple(tup);
142
143         /* If replacing, get rid of old dependencies and make new ones */
144         if (is_update)
145                 deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId);
146
147         /*
148          * Install dependency on rule's relation to ensure it will go away on
149          * relation deletion.  If the rule is ON SELECT, make the dependency
150          * implicit --- this prevents deleting a view's SELECT rule.  Other kinds
151          * of rules can be AUTO.
152          */
153         myself.classId = RewriteRelationId;
154         myself.objectId = rewriteObjectId;
155         myself.objectSubId = 0;
156
157         referenced.classId = RelationRelationId;
158         referenced.objectId = eventrel_oid;
159         referenced.objectSubId = 0;
160
161         recordDependencyOn(&myself, &referenced,
162                          (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
163
164         /*
165          * Also install dependencies on objects referenced in action and qual.
166          */
167         recordDependencyOnExpr(&myself, (Node *) action, NIL,
168                                                    DEPENDENCY_NORMAL);
169
170         if (event_qual != NULL)
171         {
172                 /* Find query containing OLD/NEW rtable entries */
173                 Query      *qry = (Query *) linitial(action);
174
175                 qry = getInsertSelectQuery(qry, NULL);
176                 recordDependencyOnExpr(&myself, event_qual, qry->rtable,
177                                                            DEPENDENCY_NORMAL);
178         }
179
180         heap_close(pg_rewrite_desc, RowExclusiveLock);
181
182         return rewriteObjectId;
183 }
184
185 /*
186  * DefineRule
187  *              Execute a CREATE RULE command.
188  */
189 void
190 DefineRule(RuleStmt *stmt, const char *queryString)
191 {
192         List       *actions;
193         Node       *whereClause;
194         Oid                     relId;
195
196         /* Parse analysis ... */
197         transformRuleStmt(stmt, queryString, &actions, &whereClause);
198
199         /* ... find the relation ... */
200         relId = RangeVarGetRelid(stmt->relation, false);
201
202         /* ... and execute */
203         DefineQueryRewrite(stmt->rulename,
204                                            relId,
205                                            whereClause,
206                                            stmt->event,
207                                            stmt->instead,
208                                            stmt->replace,
209                                            actions);
210 }
211
212
213 /*
214  * DefineQueryRewrite
215  *              Create a rule
216  *
217  * This is essentially the same as DefineRule() except that the rule's
218  * action and qual have already been passed through parse analysis.
219  */
220 void
221 DefineQueryRewrite(char *rulename,
222                                    Oid event_relid,
223                                    Node *event_qual,
224                                    CmdType event_type,
225                                    bool is_instead,
226                                    bool replace,
227                                    List *action)
228 {
229         Relation        event_relation;
230         Oid                     ruleId;
231         int                     event_attno;
232         ListCell   *l;
233         Query      *query;
234         bool            RelisBecomingView = false;
235
236         /*
237          * If we are installing an ON SELECT rule, we had better grab
238          * AccessExclusiveLock to ensure no SELECTs are currently running on the
239          * event relation.      For other types of rules, it might be sufficient to
240          * grab ShareLock to lock out insert/update/delete actions.  But for now,
241          * let's just grab AccessExclusiveLock all the time.
242          */
243         event_relation = heap_open(event_relid, AccessExclusiveLock);
244
245         /*
246          * Check user has permission to apply rules to this relation.
247          */
248         if (!pg_class_ownercheck(event_relid, GetUserId()))
249                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
250                                            RelationGetRelationName(event_relation));
251
252         /*
253          * No rule actions that modify OLD or NEW
254          */
255         foreach(l, action)
256         {
257                 query = (Query *) lfirst(l);
258                 if (query->resultRelation == 0)
259                         continue;
260                 /* Don't be fooled by INSERT/SELECT */
261                 if (query != getInsertSelectQuery(query, NULL))
262                         continue;
263                 if (query->resultRelation == PRS2_OLD_VARNO)
264                         ereport(ERROR,
265                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
266                                          errmsg("rule actions on OLD are not implemented"),
267                                          errhint("Use views or triggers instead.")));
268                 if (query->resultRelation == PRS2_NEW_VARNO)
269                         ereport(ERROR,
270                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
271                                          errmsg("rule actions on NEW are not implemented"),
272                                          errhint("Use triggers instead.")));
273         }
274
275         if (event_type == CMD_SELECT)
276         {
277                 /*
278                  * Rules ON SELECT are restricted to view definitions
279                  *
280                  * So there cannot be INSTEAD NOTHING, ...
281                  */
282                 if (list_length(action) == 0)
283                         ereport(ERROR,
284                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
285                            errmsg("INSTEAD NOTHING rules on SELECT are not implemented"),
286                                          errhint("Use views instead.")));
287
288                 /*
289                  * ... there cannot be multiple actions, ...
290                  */
291                 if (list_length(action) > 1)
292                         ereport(ERROR,
293                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
294                                          errmsg("multiple actions for rules on SELECT are not implemented")));
295
296                 /*
297                  * ... the one action must be a SELECT, ...
298                  */
299                 query = (Query *) linitial(action);
300                 if (!is_instead ||
301                         query->commandType != CMD_SELECT ||
302                         query->utilityStmt != NULL ||
303                         query->intoClause != NULL)
304                         ereport(ERROR,
305                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
306                                  errmsg("rules on SELECT must have action INSTEAD SELECT")));
307
308                 /*
309                  * ... there can be no rule qual, ...
310                  */
311                 if (event_qual != NULL)
312                         ereport(ERROR,
313                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
314                                          errmsg("event qualifications are not implemented for rules on SELECT")));
315
316                 /*
317                  * ... the targetlist of the SELECT action must exactly match the
318                  * event relation, ...
319                  */
320                 checkRuleResultList(query->targetList,
321                                                         RelationGetDescr(event_relation),
322                                                         true);
323
324                 /*
325                  * ... there must not be another ON SELECT rule already ...
326                  */
327                 if (!replace && event_relation->rd_rules != NULL)
328                 {
329                         int                     i;
330
331                         for (i = 0; i < event_relation->rd_rules->numLocks; i++)
332                         {
333                                 RewriteRule *rule;
334
335                                 rule = event_relation->rd_rules->rules[i];
336                                 if (rule->event == CMD_SELECT)
337                                         ereport(ERROR,
338                                                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
339                                                    errmsg("\"%s\" is already a view",
340                                                                   RelationGetRelationName(event_relation))));
341                         }
342                 }
343
344                 /*
345                  * ... and finally the rule must be named _RETURN.
346                  */
347                 if (strcmp(rulename, ViewSelectRuleName) != 0)
348                 {
349                         /*
350                          * In versions before 7.3, the expected name was _RETviewname. For
351                          * backwards compatibility with old pg_dump output, accept that
352                          * and silently change it to _RETURN.  Since this is just a quick
353                          * backwards-compatibility hack, limit the number of characters
354                          * checked to a few less than NAMEDATALEN; this saves having to
355                          * worry about where a multibyte character might have gotten
356                          * truncated.
357                          */
358                         if (strncmp(rulename, "_RET", 4) != 0 ||
359                                 strncmp(rulename + 4, RelationGetRelationName(event_relation),
360                                                 NAMEDATALEN - 4 - 4) != 0)
361                                 ereport(ERROR,
362                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
363                                                  errmsg("view rule for \"%s\" must be named \"%s\"",
364                                                                 RelationGetRelationName(event_relation),
365                                                                 ViewSelectRuleName)));
366                         rulename = pstrdup(ViewSelectRuleName);
367                 }
368
369                 /*
370                  * Are we converting a relation to a view?
371                  *
372                  * If so, check that the relation is empty because the storage for the
373                  * relation is going to be deleted.  Also insist that the rel not have
374                  * any triggers, indexes, or child tables.
375                  */
376                 if (event_relation->rd_rel->relkind != RELKIND_VIEW)
377                 {
378                         HeapScanDesc scanDesc;
379
380                         scanDesc = heap_beginscan(event_relation, SnapshotNow, 0, NULL);
381                         if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
382                                 ereport(ERROR,
383                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
384                                                  errmsg("could not convert table \"%s\" to a view because it is not empty",
385                                                                 RelationGetRelationName(event_relation))));
386                         heap_endscan(scanDesc);
387
388                         if (event_relation->rd_rel->reltriggers != 0)
389                                 ereport(ERROR,
390                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
391                                                  errmsg("could not convert table \"%s\" to a view because it has triggers",
392                                                                 RelationGetRelationName(event_relation)),
393                                                  errhint("In particular, the table cannot be involved in any foreign key relationships.")));
394
395                         if (event_relation->rd_rel->relhasindex)
396                                 ereport(ERROR,
397                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
398                                                  errmsg("could not convert table \"%s\" to a view because it has indexes",
399                                                                 RelationGetRelationName(event_relation))));
400
401                         if (event_relation->rd_rel->relhassubclass)
402                                 ereport(ERROR,
403                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
404                                                  errmsg("could not convert table \"%s\" to a view because it has child tables",
405                                                                 RelationGetRelationName(event_relation))));
406
407                         RelisBecomingView = true;
408                 }
409         }
410         else
411         {
412                 /*
413                  * For non-SELECT rules, a RETURNING list can appear in at most one of
414                  * the actions ... and there can't be any RETURNING list at all in a
415                  * conditional or non-INSTEAD rule.  (Actually, there can be at most
416                  * one RETURNING list across all rules on the same event, but it seems
417                  * best to enforce that at rule expansion time.)  If there is a
418                  * RETURNING list, it must match the event relation.
419                  */
420                 bool            haveReturning = false;
421
422                 foreach(l, action)
423                 {
424                         query = (Query *) lfirst(l);
425
426                         if (!query->returningList)
427                                 continue;
428                         if (haveReturning)
429                                 ereport(ERROR,
430                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
431                                   errmsg("cannot have multiple RETURNING lists in a rule")));
432                         haveReturning = true;
433                         if (event_qual != NULL)
434                                 ereport(ERROR,
435                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
436                                                  errmsg("RETURNING lists are not supported in conditional rules")));
437                         if (!is_instead)
438                                 ereport(ERROR,
439                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
440                                                  errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
441                         checkRuleResultList(query->returningList,
442                                                                 RelationGetDescr(event_relation),
443                                                                 false);
444                 }
445         }
446
447         /*
448          * This rule is allowed - prepare to install it.
449          */
450         event_attno = -1;
451
452         /* discard rule if it's null action and not INSTEAD; it's a no-op */
453         if (action != NIL || is_instead)
454         {
455                 ruleId = InsertRule(rulename,
456                                                         event_type,
457                                                         event_relid,
458                                                         event_attno,
459                                                         is_instead,
460                                                         event_qual,
461                                                         action,
462                                                         replace);
463
464                 /*
465                  * Set pg_class 'relhasrules' field TRUE for event relation. If
466                  * appropriate, also modify the 'relkind' field to show that the
467                  * relation is now a view.
468                  *
469                  * Important side effect: an SI notice is broadcast to force all
470                  * backends (including me!) to update relcache entries with the new
471                  * rule.
472                  */
473                 SetRelationRuleStatus(event_relid, true, RelisBecomingView);
474         }
475
476         /*
477          * IF the relation is becoming a view, delete the storage files associated
478          * with it.  NB: we had better have AccessExclusiveLock to do this ...
479          *
480          * XXX what about getting rid of its TOAST table?  For now, we don't.
481          */
482         if (RelisBecomingView)
483         {
484                 ForkNumber forknum;
485
486                 RelationOpenSmgr(event_relation);
487                 for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
488                         if (smgrexists(event_relation->rd_smgr, forknum))
489                                 smgrscheduleunlink(event_relation->rd_smgr, forknum,
490                                                                    event_relation->rd_istemp);
491                 RelationCloseSmgr(event_relation);
492         }
493
494         /* Close rel, but keep lock till commit... */
495         heap_close(event_relation, NoLock);
496 }
497
498 /*
499  * checkRuleResultList
500  *              Verify that targetList produces output compatible with a tupledesc
501  *
502  * The targetList might be either a SELECT targetlist, or a RETURNING list;
503  * isSelect tells which.  (This is mostly used for choosing error messages,
504  * but also we don't enforce column name matching for RETURNING.)
505  */
506 static void
507 checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect)
508 {
509         ListCell   *tllist;
510         int                     i;
511
512         i = 0;
513         foreach(tllist, targetList)
514         {
515                 TargetEntry *tle = (TargetEntry *) lfirst(tllist);
516                 int32           tletypmod;
517                 Form_pg_attribute attr;
518                 char       *attname;
519
520                 /* resjunk entries may be ignored */
521                 if (tle->resjunk)
522                         continue;
523                 i++;
524                 if (i > resultDesc->natts)
525                         ereport(ERROR,
526                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
527                                          isSelect ?
528                                    errmsg("SELECT rule's target list has too many entries") :
529                                          errmsg("RETURNING list has too many entries")));
530
531                 attr = resultDesc->attrs[i - 1];
532                 attname = NameStr(attr->attname);
533
534                 /*
535                  * Disallow dropped columns in the relation.  This won't happen in the
536                  * cases we actually care about (namely creating a view via CREATE
537                  * TABLE then CREATE RULE, or adding a RETURNING rule to a view).
538                  * Trying to cope with it is much more trouble than it's worth,
539                  * because we'd have to modify the rule to insert dummy NULLs at the
540                  * right positions.
541                  */
542                 if (attr->attisdropped)
543                         ereport(ERROR,
544                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
545                                          errmsg("cannot convert relation containing dropped columns to view")));
546
547                 if (isSelect && strcmp(tle->resname, attname) != 0)
548                         ereport(ERROR,
549                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
550                                          errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i, attname)));
551
552                 if (attr->atttypid != exprType((Node *) tle->expr))
553                         ereport(ERROR,
554                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
555                                          isSelect ?
556                                          errmsg("SELECT rule's target entry %d has different type from column \"%s\"",
557                                                         i, attname) :
558                                          errmsg("RETURNING list's entry %d has different type from column \"%s\"",
559                                                         i, attname)));
560
561                 /*
562                  * Allow typmods to be different only if one of them is -1, ie,
563                  * "unspecified".  This is necessary for cases like "numeric", where
564                  * the table will have a filled-in default length but the select
565                  * rule's expression will probably have typmod = -1.
566                  */
567                 tletypmod = exprTypmod((Node *) tle->expr);
568                 if (attr->atttypmod != tletypmod &&
569                         attr->atttypmod != -1 && tletypmod != -1)
570                         ereport(ERROR,
571                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
572                                          isSelect ?
573                                          errmsg("SELECT rule's target entry %d has different size from column \"%s\"",
574                                                         i, attname) :
575                                          errmsg("RETURNING list's entry %d has different size from column \"%s\"",
576                                                         i, attname)));
577         }
578
579         if (i != resultDesc->natts)
580                 ereport(ERROR,
581                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
582                                  isSelect ?
583                                  errmsg("SELECT rule's target list has too few entries") :
584                                  errmsg("RETURNING list has too few entries")));
585 }
586
587 /*
588  * setRuleCheckAsUser
589  *              Recursively scan a query or expression tree and set the checkAsUser
590  *              field to the given userid in all rtable entries.
591  *
592  * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD*
593  * RTE entry will be overridden when the view rule is expanded, and the
594  * checkAsUser field of the *NEW* entry is irrelevant because that entry's
595  * requiredPerms bits will always be zero.      However, for other types of rules
596  * it's important to set these fields to match the rule owner.  So we just set
597  * them always.
598  */
599 void
600 setRuleCheckAsUser(Node *node, Oid userid)
601 {
602         (void) setRuleCheckAsUser_walker(node, &userid);
603 }
604
605 static bool
606 setRuleCheckAsUser_walker(Node *node, Oid *context)
607 {
608         if (node == NULL)
609                 return false;
610         if (IsA(node, Query))
611         {
612                 setRuleCheckAsUser_Query((Query *) node, *context);
613                 return false;
614         }
615         return expression_tree_walker(node, setRuleCheckAsUser_walker,
616                                                                   (void *) context);
617 }
618
619 static void
620 setRuleCheckAsUser_Query(Query *qry, Oid userid)
621 {
622         ListCell   *l;
623
624         /* Set all the RTEs in this query node */
625         foreach(l, qry->rtable)
626         {
627                 RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
628
629                 if (rte->rtekind == RTE_SUBQUERY)
630                 {
631                         /* Recurse into subquery in FROM */
632                         setRuleCheckAsUser_Query(rte->subquery, userid);
633                 }
634                 else
635                         rte->checkAsUser = userid;
636         }
637
638         /* Recurse into subquery-in-WITH */
639         foreach(l, qry->cteList)
640         {
641                 CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
642
643                 setRuleCheckAsUser_Query((Query *) cte->ctequery, userid);
644         }
645
646         /* If there are sublinks, search for them and process their RTEs */
647         if (qry->hasSubLinks)
648                 query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid,
649                                                   QTW_IGNORE_RC_SUBQUERIES);
650 }
651
652
653 /*
654  * Change the firing semantics of an existing rule.
655  */
656 void
657 EnableDisableRule(Relation rel, const char *rulename,
658                                   char fires_when)
659 {
660         Relation        pg_rewrite_desc;
661         Oid                     owningRel = RelationGetRelid(rel);
662         Oid                     eventRelationOid;
663         HeapTuple       ruletup;
664         bool            changed = false;
665
666         /*
667          * Find the rule tuple to change.
668          */
669         pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
670         ruletup = SearchSysCacheCopy(RULERELNAME,
671                                                                  ObjectIdGetDatum(owningRel),
672                                                                  PointerGetDatum(rulename),
673                                                                  0, 0);
674         if (!HeapTupleIsValid(ruletup))
675                 ereport(ERROR,
676                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
677                                  errmsg("rule \"%s\" for relation \"%s\" does not exist",
678                                                 rulename, get_rel_name(owningRel))));
679
680         /*
681          * Verify that the user has appropriate permissions.
682          */
683         eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_class;
684         Assert(eventRelationOid == owningRel);
685         if (!pg_class_ownercheck(eventRelationOid, GetUserId()))
686                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
687                                            get_rel_name(eventRelationOid));
688
689         /*
690          * Change ev_enabled if it is different from the desired new state.
691          */
692         if (DatumGetChar(((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled) !=
693                 fires_when)
694         {
695                 ((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_enabled =
696                         CharGetDatum(fires_when);
697                 simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);
698
699                 /* keep system catalog indexes current */
700                 CatalogUpdateIndexes(pg_rewrite_desc, ruletup);
701
702                 changed = true;
703         }
704
705         heap_freetuple(ruletup);
706         heap_close(pg_rewrite_desc, RowExclusiveLock);
707
708         /*
709          * If we changed anything, broadcast a SI inval message to force each
710          * backend (including our own!) to rebuild relation's relcache entry.
711          * Otherwise they will fail to apply the change promptly.
712          */
713         if (changed)
714                 CacheInvalidateRelcache(rel);
715 }
716
717
718 /*
719  * Rename an existing rewrite rule.
720  *
721  * This is unused code at the moment.  Note that it lacks a permissions check.
722  */
723 #ifdef NOT_USED
724 void
725 RenameRewriteRule(Oid owningRel, const char *oldName,
726                                   const char *newName)
727 {
728         Relation        pg_rewrite_desc;
729         HeapTuple       ruletup;
730
731         pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
732
733         ruletup = SearchSysCacheCopy(RULERELNAME,
734                                                                  ObjectIdGetDatum(owningRel),
735                                                                  PointerGetDatum(oldName),
736                                                                  0, 0);
737         if (!HeapTupleIsValid(ruletup))
738                 ereport(ERROR,
739                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
740                                  errmsg("rule \"%s\" for relation \"%s\" does not exist",
741                                                 oldName, get_rel_name(owningRel))));
742
743         /* should not already exist */
744         if (IsDefinedRewriteRule(owningRel, newName))
745                 ereport(ERROR,
746                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
747                                  errmsg("rule \"%s\" for relation \"%s\" already exists",
748                                                 newName, get_rel_name(owningRel))));
749
750         namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName);
751
752         simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);
753
754         /* keep system catalog indexes current */
755         CatalogUpdateIndexes(pg_rewrite_desc, ruletup);
756
757         heap_freetuple(ruletup);
758         heap_close(pg_rewrite_desc, RowExclusiveLock);
759 }
760
761 #endif