OSDN Git Service

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