]> granicus.if.org Git - postgresql/commitdiff
Use a private memory context to store rule information in each relcache
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Jun 2000 07:04:23 +0000 (07:04 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Jun 2000 07:04:23 +0000 (07:04 +0000)
entry that has rules.  This allows us to release the rule parsetrees
on relcache flush without needing a working freeObject() routine.
Formerly, the rule trees were leaked permanently at relcache flush.
Also, clean up handling of rule creation and deletion --- there was
not sufficient locking of the relation being modified, and there was
no reliable notification of other backends that a relcache reload
was needed.  Also, clean up relcache.c code so that scans of system
tables needed to load a relcache entry are done in the caller's
memory context, not in CacheMemoryContext.  This prevents any
un-pfreed memory from those scans from becoming a permanent memory
leak.

src/backend/catalog/index.c
src/backend/commands/trigger.c
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteRemove.c
src/backend/rewrite/rewriteSupport.c
src/backend/utils/cache/relcache.c
src/include/catalog/index.h
src/include/rewrite/rewriteSupport.h
src/include/utils/rel.h

index c73761899682d8d977fc2ebcb283bece1295cfe7..2c5ff64daba9d9d658d0cbeb56ed95d3e2ed399d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.120 2000/06/28 03:31:23 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.121 2000/06/30 07:04:17 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -369,16 +369,21 @@ ConstructTupleDescriptor(Oid heapoid,
 
 /* ----------------------------------------------------------------
  * AccessMethodObjectIdGetForm
- *             Returns the formated access method tuple given its object identifier.
+ *             Returns an access method tuple given its object identifier,
+ *             or NULL if no such AM tuple can be found.
  *
- * XXX ADD INDEXING
+ * Scanning is done using CurrentMemoryContext as working storage,
+ * but the returned tuple will be allocated in resultCxt (which is
+ * typically CacheMemoryContext).
  *
- * Note:
- *             Assumes object identifier is valid.
+ * There was a note here about adding indexing, but I don't see a need
+ * for it.  There are so few tuples in pg_am that an indexscan would
+ * surely be slower.
  * ----------------------------------------------------------------
  */
 Form_pg_am
-AccessMethodObjectIdGetForm(Oid accessMethodObjectId)
+AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
+                                                       MemoryContext resultCxt)
 {
        Relation        pg_am_desc;
        HeapScanDesc pg_am_scan;
@@ -415,10 +420,10 @@ AccessMethodObjectIdGetForm(Oid accessMethodObjectId)
        }
 
        /* ----------------
-        *      if found am tuple, then copy the form and return the copy
+        *      if found AM tuple, then copy it into resultCxt and return the copy
         * ----------------
         */
-       aform = (Form_pg_am) palloc(sizeof *aform);
+       aform = (Form_pg_am) MemoryContextAlloc(resultCxt, sizeof *aform);
        memcpy(aform, GETSTRUCT(pg_am_tuple), sizeof *aform);
 
        heap_endscan(pg_am_scan);
@@ -434,22 +439,8 @@ AccessMethodObjectIdGetForm(Oid accessMethodObjectId)
 static void
 ConstructIndexReldesc(Relation indexRelation, Oid amoid)
 {
-       MemoryContext oldcxt;
-
-       /* ----------------
-        *        here we make certain to allocate the access method
-        *        tuple within the cache context lest it vanish when the
-        *        context changes
-        * ----------------
-        */
-       if (!CacheMemoryContext)
-               CreateCacheMemoryContext();
-
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
-       indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid);
-
-       MemoryContextSwitchTo(oldcxt);
+       indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid,
+                                                                                                          CacheMemoryContext);
 
        /* ----------------
         *       XXX missing the initialization of some other fields
index fbb5a694b88572763c9176bb8c394f3fa47b8269..075f3a508b5520143ad4d9031dfa773b4e59ba48 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.70 2000/06/28 03:31:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.71 2000/06/30 07:04:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -431,10 +431,17 @@ RelationRemoveTriggers(Relation rel)
        heap_close(tgrel, RowExclusiveLock);
 }
 
+/*
+ * Build trigger data to attach to the given relcache entry.
+ *
+ * Note that trigger data must be allocated in CacheMemoryContext
+ * to ensure it survives as long as the relcache entry.  But we
+ * are probably running in a less long-lived working context.
+ */
 void
 RelationBuildTriggers(Relation relation)
 {
-       TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
+       TriggerDesc *trigdesc;
        int                     ntrigs = relation->rd_rel->reltriggers;
        Trigger    *triggers = NULL;
        Trigger    *build;
@@ -453,6 +460,8 @@ RelationBuildTriggers(Relation relation)
        int                     found;
        bool            hasindex;
 
+       trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                 sizeof(TriggerDesc));
        MemSet(trigdesc, 0, sizeof(TriggerDesc));
 
        ScanKeyEntryInitialize(&skey,
@@ -499,13 +508,16 @@ RelationBuildTriggers(Relation relation)
                pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
 
                if (triggers == NULL)
-                       triggers = (Trigger *) palloc(sizeof(Trigger));
+                       triggers = (Trigger *) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                         sizeof(Trigger));
                else
-                       triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger));
+                       triggers = (Trigger *) repalloc(triggers,
+                                                                                       (found + 1) * sizeof(Trigger));
                build = &(triggers[found]);
 
                build->tgoid = htup->t_data->t_oid;
-               build->tgname = nameout(&pg_trigger->tgname);
+               build->tgname = MemoryContextStrdup(CacheMemoryContext,
+                                                                                       nameout(&pg_trigger->tgname));
                build->tgfoid = pg_trigger->tgfoid;
                build->tgfunc.fn_oid = InvalidOid; /* mark FmgrInfo as uninitialized */
                build->tgtype = pg_trigger->tgtype;
@@ -514,7 +526,8 @@ RelationBuildTriggers(Relation relation)
                build->tgdeferrable = pg_trigger->tgdeferrable;
                build->tginitdeferred = pg_trigger->tginitdeferred;
                build->tgnargs = pg_trigger->tgnargs;
-               memcpy(build->tgattr, &(pg_trigger->tgattr), FUNC_MAX_ARGS * sizeof(int16));
+               memcpy(build->tgattr, &(pg_trigger->tgattr),
+                          FUNC_MAX_ARGS * sizeof(int16));
                val = (struct varlena *) fastgetattr(htup,
                                                                                         Anum_pg_trigger_tgargs,
                                                                                         tgrel->rd_att, &isnull);
@@ -533,10 +546,13 @@ RelationBuildTriggers(Relation relation)
                                elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %s",
                                         RelationGetRelationName(relation));
                        p = (char *) VARDATA(val);
-                       build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
+                       build->tgargs = (char **)
+                               MemoryContextAlloc(CacheMemoryContext,
+                                                                  build->tgnargs * sizeof(char *));
                        for (i = 0; i < build->tgnargs; i++)
                        {
-                               build->tgargs[i] = pstrdup(p);
+                               build->tgargs[i] = MemoryContextStrdup(CacheMemoryContext,
+                                                                                                          p);
                                p += strlen(p) + 1;
                        }
                }
@@ -611,7 +627,8 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
        {
                tp = &(t[TRIGGER_EVENT_INSERT]);
                if (*tp == NULL)
-                       *tp = (Trigger **) palloc(sizeof(Trigger *));
+                       *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                 sizeof(Trigger *));
                else
                        *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
                                                                                sizeof(Trigger *));
@@ -623,7 +640,8 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
        {
                tp = &(t[TRIGGER_EVENT_DELETE]);
                if (*tp == NULL)
-                       *tp = (Trigger **) palloc(sizeof(Trigger *));
+                       *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                 sizeof(Trigger *));
                else
                        *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
                                                                                sizeof(Trigger *));
@@ -635,7 +653,8 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
        {
                tp = &(t[TRIGGER_EVENT_UPDATE]);
                if (*tp == NULL)
-                       *tp = (Trigger **) palloc(sizeof(Trigger *));
+                       *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                 sizeof(Trigger *));
                else
                        *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
                                                                                sizeof(Trigger *));
@@ -1023,10 +1042,9 @@ ltrmark:;
 
 /* ----------
  * Internal data to the deferred trigger mechanism is held
- * during entire session in a global memor created at startup and
- * over statements/commands in a separate global memory which
- * is created at transaction start and destroyed at transaction
- * end.
+ * during entire session in a global context created at startup and
+ * over statements/commands in a separate context which
+ * is created at transaction start and destroyed at transaction end.
  * ----------
  */
 static MemoryContext deftrig_gcxt = NULL;
index 08351fe66969fc03ed9751270e679c74381248af..d92190f652419a060afddf75c95b09a14a39841c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.47 2000/06/28 03:31:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.48 2000/06/30 07:04:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * InsertRule -
  *       takes the arguments and inserts them as attributes into the system
  *       relation "pg_rewrite"
- *
- *             ARGS :  rulname                 -               name of the rule
- *                             evtype                  -               one of RETRIEVE,REPLACE,DELETE,APPEND
- *                             evobj                   -               name of relation
- *                             evslot                  -               comma delimited list of slots
- *                                                                             if null => multi-attr rule
- *                             evinstead               -               is an instead rule
- *                             actiontree              -               parsetree(s) of rule action
  */
 static Oid
 InsertRule(char *rulname,
                   int evtype,
-                  char *evobj,
-                  char *evslot,
-                  char *evqual,
+                  Oid eventrel_oid,
+                  AttrNumber evslot_index,
                   bool evinstead,
+                  char *evqual,
                   char *actiontree)
 {
-       Relation        eventrel;
-       Oid                     eventrel_oid;
-       AttrNumber      evslot_index;
        int                     i;
        Datum           values[Natts_pg_rewrite];
        char            nulls[Natts_pg_rewrite];
@@ -59,21 +48,6 @@ InsertRule(char *rulname,
        HeapTuple       tup;
        Oid                     rewriteObjectId;
 
-       eventrel = heap_openr(evobj, AccessShareLock);
-       eventrel_oid = RelationGetRelid(eventrel);
-
-       /*
-        * if the slotname is null, we know that this is a multi-attr rule
-        */
-       if (evslot == NULL)
-               evslot_index = -1;
-       else
-               evslot_index = attnameAttNum(eventrel, evslot);
-       heap_close(eventrel, AccessShareLock);
-
-       if (evqual == NULL)
-               evqual = "<>";
-
        if (IsDefinedRewriteRule(rulname))
                elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
                         rulname);
@@ -177,17 +151,27 @@ DefineQueryRewrite(RuleStmt *stmt)
        Node       *event_qual = stmt->whereClause;
        bool            is_instead = stmt->instead;
        List       *action = stmt->actions;
-       Relation        event_relation = NULL;
+       Relation        event_relation;
+       Oid                     ev_relid;
        Oid                     ruleId;
-       Oid                     ev_relid = 0;
        char       *eslot_string = NULL;
-       int                     event_attno = 0;
-       Oid                     event_attype = 0;
+       int                     event_attno;
+       Oid                     event_attype;
        char       *actionP,
                           *event_qualP;
        List       *l;
        Query      *query;
 
+       /*
+        * If we are installing an ON SELECT rule, we had better grab
+        * AccessExclusiveLock to ensure no SELECTs are currently running on
+        * the event relation.  For other types of rules, it might be sufficient
+        * to grab ShareLock to lock out insert/update/delete actions.  But
+        * for now, let's just grab AccessExclusiveLock all the time.
+        */
+       event_relation = heap_openr(event_obj->relname, AccessExclusiveLock);
+       ev_relid = RelationGetRelid(event_relation);
+
        /* ----------
         * The current rewrite handler is known to work on relation level
         * rules only. And for SELECT events, it expects one non-nothing
@@ -209,19 +193,18 @@ DefineQueryRewrite(RuleStmt *stmt)
        /*
         * No rule actions that modify OLD or NEW
         */
-       if (action != NIL)
-               foreach(l, action)
+       foreach(l, action)
        {
                query = (Query *) lfirst(l);
                if (query->resultRelation == 1)
                {
-                       elog(NOTICE, "rule actions on OLD currently not supported");
-                       elog(ERROR, " use views or triggers instead");
+                       elog(ERROR, "rule actions on OLD currently not supported"
+                                "\n\tuse views or triggers instead");
                }
                if (query->resultRelation == 2)
                {
-                       elog(NOTICE, "rule actions on NEW currently not supported");
-                       elog(ERROR, " use triggers instead");
+                       elog(ERROR, "rule actions on NEW currently not supported"
+                                "\n\tuse triggers instead");
                }
        }
 
@@ -242,8 +225,8 @@ DefineQueryRewrite(RuleStmt *stmt)
                 */
                if (length(action) == 0)
                {
-                       elog(NOTICE, "instead nothing rules on select currently not supported");
-                       elog(ERROR, " use views instead");
+                       elog(ERROR, "instead nothing rules on select currently not supported"
+                                "\n\tuse views instead");
                }
 
                /*
@@ -265,8 +248,6 @@ DefineQueryRewrite(RuleStmt *stmt)
                 * ... the targetlist of the SELECT action must exactly match the
                 * event relation, ...
                 */
-               event_relation = heap_openr(event_obj->relname, AccessShareLock);
-
                if (event_relation->rd_att->natts != length(query->targetList))
                        elog(ERROR, "select rules target list must match event relations structure");
 
@@ -275,7 +256,7 @@ DefineQueryRewrite(RuleStmt *stmt)
                        tle = (TargetEntry *) nth(i - 1, query->targetList);
                        resdom = tle->resdom;
                        attr = event_relation->rd_att->attrs[i - 1];
-                       attname = pstrdup(NameStr(attr->attname));
+                       attname = NameStr(attr->attname);
 
                        if (strcmp(resdom->resname, attname) != 0)
                                elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
@@ -303,8 +284,6 @@ DefineQueryRewrite(RuleStmt *stmt)
                        }
                }
 
-               heap_close(event_relation, AccessShareLock);
-
                /*
                 * LIMIT in view is not supported
                 */
@@ -337,62 +316,46 @@ DefineQueryRewrite(RuleStmt *stmt)
        /*
         * This rule is allowed - install it.
         */
-
-       event_relation = heap_openr(event_obj->relname, AccessShareLock);
-       ev_relid = RelationGetRelid(event_relation);
-
        if (eslot_string == NULL)
        {
                event_attno = -1;
-               event_attype = -1;              /* XXX - don't care */
+               event_attype = InvalidOid;
        }
        else
        {
                event_attno = attnameAttNum(event_relation, eslot_string);
                event_attype = attnumTypeId(event_relation, event_attno);
        }
-       heap_close(event_relation, AccessShareLock);
 
        /* fix bug about instead nothing */
        ValidateRule(event_type, event_obj->relname,
                                 eslot_string, event_qual, &action,
                                 is_instead, event_attype);
 
-       if (action == NULL)
-       {
-               if (!is_instead)
-                       return;                         /* doesn't do anything */
-
-               event_qualP = nodeToString(event_qual);
-
-               ruleId = InsertRule(stmt->rulename,
-                                                       event_type,
-                                                       event_obj->relname,
-                                                       eslot_string,
-                                                       event_qualP,
-                                                       true,
-                                                       "<>");
-               prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, TRUE,
-                                                  event_qual, NIL);
-
-       }
-       else
+       /* discard rule if it's null action and not INSTEAD; it's a no-op */
+       if (action != NULL || is_instead)
        {
                event_qualP = nodeToString(event_qual);
                actionP = nodeToString(action);
 
                ruleId = InsertRule(stmt->rulename,
                                                        event_type,
-                                                       event_obj->relname,
-                                                       eslot_string,
-                                                       event_qualP,
+                                                       ev_relid,
+                                                       event_attno,
                                                        is_instead,
+                                                       event_qualP,
                                                        actionP);
 
-               /* what is the max size of type text? XXX -- glass */
-               if (length(action) > 15)
-                       elog(ERROR, "max # of actions exceeded");
-               prs2_addToRelation(ev_relid, ruleId, event_type, event_attno,
-                                                  is_instead, event_qual, action);
+               /*
+                * Set pg_class 'relhasrules' field TRUE for event relation.
+                *
+                * Important side effect: an SI notice is broadcast to force all
+                * backends (including me!) to update relcache entries with the new
+                * rule.
+                */
+               setRelhasrulesInRelation(ev_relid, true);
        }
+
+       /* Close rel, but keep lock till commit... */
+       heap_close(event_relation, NoLock);
 }
index 255190ebf529ce5ce01255e6fd9190f2324a0107..acd62c9c70e9082065b795b5c11418a5e9d2c455 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.76 2000/06/15 03:32:22 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.77 2000/06/30 07:04:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1332,11 +1332,11 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
        rt_entry = rt_fetch(result_relation, parsetree->rtable);
        rt_entry_relation = heap_openr(rt_entry->relname, AccessShareLock);
        rt_entry_locks = rt_entry_relation->rd_rules;
-       heap_close(rt_entry_relation, AccessShareLock);
 
        if (rt_entry_locks != NULL)
        {
-               List       *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree);
+               List       *locks = matchLocks(event, rt_entry_locks,
+                                                                          result_relation, parsetree);
 
                product_queries = fireRules(parsetree,
                                                                        result_relation,
@@ -1346,13 +1346,15 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
                                                                        qual_products);
        }
 
+       heap_close(rt_entry_relation, AccessShareLock);
+
        return product_queries;
 }
 
 
 /*
  * to avoid infinite recursion, we restrict the number of times a query
- * can be rewritten. Detecting cycles is left for the reader as an excercise.
+ * can be rewritten. Detecting cycles is left for the reader as an exercise.
  */
 #ifndef REWRITE_INVOKE_MAX
 #define REWRITE_INVOKE_MAX             10
@@ -1373,8 +1375,6 @@ deepRewriteQuery(Query *parsetree)
        bool            instead;
        List       *qual_products = NIL;
 
-
-
        if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
        {
                elog(ERROR, "query rewritten %d times, may contain cycles",
index 13a07adbd8c864974d8220cc5bfbceab706433de..d50e1049097bbe006df356f111b38e0dd71214eb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.37 2000/05/28 17:56:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.38 2000/06/30 07:04:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,30 +52,20 @@ RewriteGetRuleEventRel(char *rulename)
        return NameStr(((Form_pg_class) GETSTRUCT(htup))->relname);
 }
 
-/* ----------------------------------------------------------------
- *
+/*
  * RemoveRewriteRule
  *
  * Delete a rule given its rulename.
- *
- * There are three steps.
- *      1) Find the corresponding tuple in 'pg_rewrite' relation.
- *             Find the rule Id (i.e. the Oid of the tuple) and finally delete
- *             the tuple.
- *      3) Delete the locks from the 'pg_class' relation.
- *
- *
- * ----------------------------------------------------------------
  */
 void
 RemoveRewriteRule(char *ruleName)
 {
-       Relation        RewriteRelation = NULL;
-       HeapTuple       tuple = NULL;
-       Oid                     ruleId = (Oid) 0;
-       Oid                     eventRelationOid = (Oid) NULL;
-       Datum           eventRelationOidDatum = (Datum) NULL;
-       bool            isNull = false;
+       Relation        RewriteRelation;
+       Relation        event_relation;
+       HeapTuple       tuple;
+       Oid                     ruleId;
+       Oid                     eventRelationOid;
+       bool            hasMoreRules;
 
        /*
         * Open the pg_rewrite relation.
@@ -83,7 +73,7 @@ RemoveRewriteRule(char *ruleName)
        RewriteRelation = heap_openr(RewriteRelationName, RowExclusiveLock);
 
        /*
-        * Scan the RuleRelation ('pg_rewrite') until we find a tuple
+        * Find the tuple for the target rule.
         */
        tuple = SearchSysCacheTupleCopy(RULENAME,
                                                                        PointerGetDatum(ruleName),
@@ -99,44 +89,49 @@ RemoveRewriteRule(char *ruleName)
        }
 
        /*
-        * Store the OID of the rule (i.e. the tuple's OID) and the event
+        * Save the OID of the rule (i.e. the tuple's OID) and the event
         * relation's OID
         */
        ruleId = tuple->t_data->t_oid;
-       eventRelationOidDatum = heap_getattr(tuple,
-                                                                                Anum_pg_rewrite_ev_class,
-                                                                          RelationGetDescr(RewriteRelation),
-                                                                                &isNull);
-       if (isNull)
-       {
-               /* XXX strange!!! */
-               heap_freetuple(tuple);
-               elog(ERROR, "RemoveRewriteRule: internal error; null event target relation!");
-       }
-       eventRelationOid = DatumGetObjectId(eventRelationOidDatum);
+       eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
 
        /*
-        * Now delete the relation level locks from the updated relation.
-        * (Make sure we do this before we remove the rule from pg_rewrite.
-        * Otherwise, heap_openr on eventRelationOid which reads pg_rwrite for
-        * the rules will fail.)
+        * We had better grab AccessExclusiveLock so that we know no other
+        * rule additions/deletions are going on for this relation.  Else
+        * we cannot set relhasrules correctly.  Besides, we don't want to
+        * be changing the ruleset while queries are executing on the rel.
         */
-       prs2_deleteFromRelation(eventRelationOid, ruleId);
+       event_relation = heap_open(eventRelationOid, AccessExclusiveLock);
+
+       hasMoreRules = event_relation->rd_rules != NULL &&
+               event_relation->rd_rules->numLocks > 1;
 
        /*
         * Delete any comments associated with this rule
-        *
         */
-
        DeleteComments(ruleId);
 
        /*
-        * Now delete the tuple...
+        * Now delete the pg_rewrite tuple for the rule
         */
        heap_delete(RewriteRelation, &tuple->t_self, NULL);
 
        heap_freetuple(tuple);
+
        heap_close(RewriteRelation, RowExclusiveLock);
+
+       /*
+        * Set pg_class 'relhasrules' field correctly for event relation.
+        *
+        * Important side effect: an SI notice is broadcast to force all
+        * backends (including me!) to update relcache entries with the
+        * new rule set.  Therefore, must do this even if relhasrules is
+        * still true!
+        */
+       setRelhasrulesInRelation(eventRelationOid, hasMoreRules);
+
+       /* Close rel, but keep lock till commit... */
+       heap_close(event_relation, NoLock);
 }
 
 /*
index 113e0b73c596a0121f99e5e4ae2e96ede0ff5770..fe87f5693878536edbd3b636b2aca0bd77e6913a 100644 (file)
@@ -8,13 +8,12 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.42 2000/06/28 03:31:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.43 2000/06/30 07:04:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-
 #include "access/heapam.h"
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
 #include "utils/catcache.h"
 #include "utils/syscache.h"
 
-/*
- * RuleIdGetActionInfo -
- *        given a rule oid, look it up and return the rule-event-qual and
- *        list of parsetrees for the rule (in parseTrees)
- */
-#ifdef NOT_USED
-static Node *
-RuleIdGetActionInfo(Oid ruleoid, bool *instead_flag, Query **parseTrees)
-{
-       HeapTuple       ruletuple;
-       char       *ruleaction = NULL;
-       bool            action_is_null = false;
-       bool            instead_is_null = false;
-       Relation        ruleRelation = NULL;
-       TupleDesc       ruleTupdesc = NULL;
-       Query      *ruleparse = NULL;
-       char       *rule_evqual_string = NULL;
-       Node       *rule_evqual = NULL;
-
-       ruleRelation = heap_openr(RewriteRelationName, AccessShareLock);
-       ruleTupdesc = RelationGetDescr(ruleRelation);
-       ruletuple = SearchSysCacheTuple(RULEOID,
-                                                                       ObjectIdGetDatum(ruleoid),
-                                                                       0, 0, 0);
-       if (ruletuple == NULL)
-               elog(ERROR, "rule %u isn't in rewrite system relation", ruleoid);
-
-       ruleaction = (char *) heap_getattr(ruletuple,
-                                                                          Anum_pg_rewrite_ev_action,
-                                                                          ruleTupdesc,
-                                                                          &action_is_null);
-       rule_evqual_string = (char *) heap_getattr(ruletuple,
-                                                                                          Anum_pg_rewrite_ev_qual,
-                                                                                  ruleTupdesc, &action_is_null);
-       *instead_flag = !!heap_getattr(ruletuple,
-                                                                  Anum_pg_rewrite_is_instead,
-                                                                  ruleTupdesc, &instead_is_null);
-
-       if (action_is_null || instead_is_null)
-               elog(ERROR, "internal error: rewrite rule not properly set up");
-
-       ruleaction = textout((struct varlena *) ruleaction);
-       rule_evqual_string = textout((struct varlena *) rule_evqual_string);
-
-       ruleparse = (Query *) stringToNode(ruleaction);
-       rule_evqual = (Node *) stringToNode(rule_evqual_string);
-
-       heap_close(ruleRelation, AccessShareLock);
-
-       *parseTrees = ruleparse;
-       return rule_evqual;
-}
-
-#endif
 
 int
 IsDefinedRewriteRule(char *ruleName)
@@ -88,7 +33,20 @@ IsDefinedRewriteRule(char *ruleName)
        return HeapTupleIsValid(tuple);
 }
 
-static void
+/*
+ * setRelhasrulesInRelation
+ *             Set the value of the relation's relhasrules field in pg_class.
+ *
+ * NOTE: caller should be holding an appropriate lock on the relation.
+ *
+ * NOTE: an important side-effect of this operation is that an SI invalidation
+ * message is sent out to all backends --- including me --- causing relcache
+ * entries to be flushed or updated with the new set of rules for the table.
+ * Therefore, we execute the update even if relhasrules has the right value
+ * already.  Possible future improvement: skip the disk update and just send
+ * an SI message in that case.
+ */
+void
 setRelhasrulesInRelation(Oid relationId, bool relhasrules)
 {
        Relation        relationRelation;
@@ -96,9 +54,7 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
        Relation        idescs[Num_pg_class_indices];
 
        /*
-        * Lock a relation given its Oid. Go to the RelationRelation (i.e.
-        * pg_relation), find the appropriate tuple, and add the specified
-        * lock to it.
+        * Find the tuple to update in pg_class, using syscache for the lookup.
         */
        relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
        tuple = SearchSysCacheTupleCopy(RELOID,
@@ -106,10 +62,11 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
                                                                        0, 0, 0);
        Assert(HeapTupleIsValid(tuple));
 
+       /* Do the update */
        ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relhasrules;
        heap_update(relationRelation, &tuple->t_self, tuple, NULL);
 
-       /* keep the catalog indices up to date */
+       /* Keep the catalog indices up to date */
        CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
        CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
        CatalogCloseIndices(Num_pg_class_indices, idescs);
@@ -117,120 +74,3 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
        heap_freetuple(tuple);
        heap_close(relationRelation, RowExclusiveLock);
 }
-
-void
-prs2_addToRelation(Oid relid,
-                                  Oid ruleId,
-                                  CmdType event_type,
-                                  AttrNumber attno,
-                                  bool isInstead,
-                                  Node *qual,
-                                  List *actions)
-{
-       Relation        relation;
-       RewriteRule *thisRule;
-       RuleLock   *rulelock;
-       MemoryContext oldcxt;
-
-       /*
-        * create an in memory RewriteRule data structure which is cached by
-        * every Relation descriptor. (see utils/cache/relcache.c)
-        */
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-       thisRule = (RewriteRule *) palloc(sizeof(RewriteRule));
-       if (qual != NULL)
-               qual = copyObject(qual);
-       if (actions != NIL)
-               actions = copyObject(actions);
-       MemoryContextSwitchTo(oldcxt);
-
-       thisRule->ruleId = ruleId;
-       thisRule->event = event_type;
-       thisRule->attrno = attno;
-       thisRule->qual = qual;
-       thisRule->actions = actions;
-       thisRule->isInstead = isInstead;
-
-       relation = heap_open(relid, AccessShareLock);
-
-       /*
-        * modify or create a RuleLock cached by Relation
-        */
-       if (relation->rd_rules == NULL)
-       {
-
-               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-               rulelock = (RuleLock *) palloc(sizeof(RuleLock));
-               rulelock->numLocks = 1;
-               rulelock->rules = (RewriteRule **) palloc(sizeof(RewriteRule *));
-               rulelock->rules[0] = thisRule;
-               relation->rd_rules = rulelock;
-               MemoryContextSwitchTo(oldcxt);
-
-               /*
-                * the fact that relation->rd_rules is NULL means the relhasrules
-                * attribute of the tuple of this relation in pg_class is false.
-                * We need to set it to true.
-                */
-               setRelhasrulesInRelation(relid, TRUE);
-       }
-       else
-       {
-               int                     numlock;
-
-               rulelock = relation->rd_rules;
-               numlock = rulelock->numLocks;
-               /* expand, for safety reasons */
-               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-               rulelock->rules = (RewriteRule **) repalloc(rulelock->rules,
-                                                                 sizeof(RewriteRule *) * (numlock + 1));
-               MemoryContextSwitchTo(oldcxt);
-               rulelock->rules[numlock] = thisRule;
-               rulelock->numLocks++;
-       }
-
-       heap_close(relation, AccessShareLock);
-}
-
-void
-prs2_deleteFromRelation(Oid relid, Oid ruleId)
-{
-       RuleLock   *rulelock;
-       Relation        relation;
-       int                     numlock;
-       int                     i;
-       MemoryContext oldcxt;
-
-       relation = heap_open(relid, AccessShareLock);
-       rulelock = relation->rd_rules;
-       Assert(rulelock != NULL);
-
-       numlock = rulelock->numLocks;
-       for (i = 0; i < numlock; i++)
-       {
-               if (rulelock->rules[i]->ruleId == ruleId)
-                       break;
-       }
-       Assert(i < numlock);
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-       pfree(rulelock->rules[i]);
-       MemoryContextSwitchTo(oldcxt);
-       if (numlock == 1)
-       {
-               relation->rd_rules = NULL;
-
-               /*
-                * we don't have rules any more, flag the relhasrules attribute of
-                * the tuple of this relation in pg_class false.
-                */
-               setRelhasrulesInRelation(relid, FALSE);
-       }
-       else
-       {
-               rulelock->rules[i] = rulelock->rules[numlock - 1];
-               rulelock->rules[numlock - 1] = NULL;
-               rulelock->numLocks--;
-       }
-
-       heap_close(relation, AccessShareLock);
-}
index b0ee20fce986f810cf90b4b61833166b4a068e79..dc69a0e508fc42a5c9b634d5f93cc144b8a37a72 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.104 2000/06/28 03:32:24 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.105 2000/06/30 07:04:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,9 +22,6 @@
  *             RelationClose                                   - close an open relation
  *
  * NOTES
- *             This file is in the process of being cleaned up
- *             before I add system attribute indexing.  -cim 1/13/91
- *
  *             The following code contains many undocumented hacks.  Please be
  *             careful....
  *
@@ -59,6 +56,7 @@
 #include "storage/smgr.h"
 #include "utils/catcache.h"
 #include "utils/fmgroids.h"
+#include "utils/memutils.h"
 #include "utils/relcache.h"
 #include "utils/temprel.h"
 
@@ -77,7 +75,7 @@ static FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log};
 /* ----------------
  *             Hash tables that index the relation cache
  *
- *             Relations are cached two ways, by name and by id,
+ *             Relations are looked up two ways, by name and by id,
  *             thus there are two hash tables for referencing them.
  * ----------------
  */
@@ -91,6 +89,12 @@ static HTAB *RelationIdCache;
  */
 static List *newlyCreatedRelns = NULL;
 
+/*
+ * This flag is false until we have prepared the critical relcache entries
+ * that are needed to do indexscans on the tables read by relcache building.
+ */
+static bool criticalRelcachesBuilt = false;
+
 
 /* ----------------
  *             RelationBuildDescInfo exists so code can be shared
@@ -211,20 +215,19 @@ static void RelationCacheAbortWalker(Relation *relationPtr, int dummy);
 static void init_irels(void);
 static void write_irels(void);
 
-static void formrdesc(char *relationName, u_int natts,
+static void formrdesc(char *relationName, int natts,
                  FormData_pg_attribute *att);
 
 static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
 static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
 static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
-static Relation AllocateRelationDesc(Relation relation, u_int natts,
-                                        Form_pg_class relp);
+static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp);
 static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
-                                          Relation relation, u_int natts);
+                                          Relation relation);
 static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
-                                 Relation relation, u_int natts);
+                                 Relation relation);
 static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
-                                 Relation relation, u_int natts);
+                                 Relation relation);
 static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
                                  Relation oldrelation);
 static void IndexedAccessMethodInitialize(Relation relation);
@@ -232,7 +235,6 @@ static void AttrDefaultFetch(Relation relation);
 static void RelCheckFetch(Relation relation);
 static List *insert_ordered_oid(List *list, Oid datum);
 
-static bool criticalRelcacheBuild = false;
 
 /* ----------------------------------------------------------------
  *             RelationIdGetRelation() and RelationNameGetRelation()
@@ -262,7 +264,7 @@ ScanPgRelation(RelationBuildDescInfo buildinfo)
         * can, and do.
         */
 
-       if (IsIgnoringSystemIndexes() || !criticalRelcacheBuild)
+       if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
                return scan_pg_rel_seq(buildinfo);
        else
                return scan_pg_rel_ind(buildinfo);
@@ -379,34 +381,23 @@ scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
  * ----------------
  */
 static Relation
-AllocateRelationDesc(Relation relation, u_int natts,
-                                        Form_pg_class relp)
+AllocateRelationDesc(Relation relation, Form_pg_class relp)
 {
+       MemoryContext oldcxt;
        Form_pg_class relationForm;
 
-       /* ----------------
-        *      Copy the relation tuple form
-        *
-        *      We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE.
-        *      relacl is NOT stored in the relcache --- there'd be little point
-        *      in it, since we don't copy the tuple's nullvalues bitmap and hence
-        *      wouldn't know if the value is valid ... bottom line is that relacl
-        *      *cannot* be retrieved from the relcache.  Get it from the syscache
-        *      if you need it.
-        * ----------------
-        */
-       relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
-
-       memcpy((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE);
+       /* Relcache entries must live in CacheMemoryContext */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
        /* ----------------
         *      allocate space for new relation descriptor, if needed
+        * ----------------
         */
        if (relation == NULL)
                relation = (Relation) palloc(sizeof(RelationData));
 
        /* ----------------
-        *      clear new reldesc
+        *      clear all fields of reldesc
         * ----------------
         */
        MemSet((char *) relation, 0, sizeof(RelationData));
@@ -414,12 +405,29 @@ AllocateRelationDesc(Relation relation, u_int natts,
        /* make sure relation is marked as having no open file yet */
        relation->rd_fd = -1;
 
-       /* initialize attribute tuple form */
-       relation->rd_att = CreateTemplateTupleDesc(natts);
+       /* ----------------
+        *      Copy the relation tuple form
+        *
+        *      We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE.
+        *      relacl is NOT stored in the relcache --- there'd be little point
+        *      in it, since we don't copy the tuple's nullvalues bitmap and hence
+        *      wouldn't know if the value is valid ... bottom line is that relacl
+        *      *cannot* be retrieved from the relcache.  Get it from the syscache
+        *      if you need it.
+        * ----------------
+        */
+       relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
 
-       /* and initialize relation tuple form */
+       memcpy((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE);
+
+       /* initialize relation tuple form */
        relation->rd_rel = relationForm;
 
+       /* and allocate attribute tuple form storage */
+       relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts);
+
+       MemoryContextSwitchTo(oldcxt);
+
        return relation;
 }
 
@@ -432,8 +440,7 @@ AllocateRelationDesc(Relation relation, u_int natts,
  */
 static void
 RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
-                                          Relation relation,
-                                          u_int natts)
+                                          Relation relation)
 {
 
        /*
@@ -442,14 +449,17 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
         * can, and do.
         */
 
-       if (IsIgnoringSystemIndexes() || !criticalRelcacheBuild)
-               build_tupdesc_seq(buildinfo, relation, natts);
+       if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
+               build_tupdesc_seq(buildinfo, relation);
        else
-               build_tupdesc_ind(buildinfo, relation, natts);
+               build_tupdesc_ind(buildinfo, relation);
 }
 
 static void
-SetConstrOfRelation(Relation relation, TupleConstr *constr, int ndef, AttrDefault *attrdef)
+SetConstrOfRelation(Relation relation,
+                                       TupleConstr *constr,
+                                       int ndef,
+                                       AttrDefault *attrdef)
 {
        if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks)
        {
@@ -471,8 +481,9 @@ SetConstrOfRelation(Relation relation, TupleConstr *constr, int ndef, AttrDefaul
                if (relation->rd_rel->relchecks > 0)    /* CHECKs */
                {
                        constr->num_check = relation->rd_rel->relchecks;
-                       constr->check = (ConstrCheck *) palloc(constr->num_check *
-                                                                                                  sizeof(ConstrCheck));
+                       constr->check = (ConstrCheck *)
+                               MemoryContextAlloc(CacheMemoryContext,
+                                                                  constr->num_check * sizeof(ConstrCheck));
                        MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
                        RelCheckFetch(relation);
                }
@@ -488,8 +499,7 @@ SetConstrOfRelation(Relation relation, TupleConstr *constr, int ndef, AttrDefaul
 
 static void
 build_tupdesc_seq(RelationBuildDescInfo buildinfo,
-                                 Relation relation,
-                                 u_int natts)
+                                 Relation relation)
 {
        HeapTuple       pg_attribute_tuple;
        Relation        pg_attribute_desc;
@@ -497,11 +507,14 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
        Form_pg_attribute attp;
        ScanKeyData key;
        int                     need;
-       TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+       TupleConstr *constr;
        AttrDefault *attrdef = NULL;
        int                     ndef = 0;
 
+       constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
+                                                                                               sizeof(TupleConstr));
        constr->has_not_null = false;
+
        /* ----------------
         *      form a scan key
         * ----------------
@@ -522,7 +535,7 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
         *      add attribute data to relation->rd_att
         * ----------------
         */
-       need = natts;
+       need = relation->rd_rel->relnatts;
 
        pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
        while (HeapTupleIsValid(pg_attribute_tuple) && need > 0)
@@ -532,12 +545,14 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
                if (attp->attnum > 0)
                {
                        relation->rd_att->attrs[attp->attnum - 1] =
-                               (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
+                               (Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                          ATTRIBUTE_TUPLE_SIZE);
 
                        memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
                                   (char *) attp,
                                   ATTRIBUTE_TUPLE_SIZE);
                        need--;
+
                        /* Update if this attribute have a constraint */
                        if (attp->attnotnull)
                                constr->has_not_null = true;
@@ -546,10 +561,12 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
                        {
                                if (attrdef == NULL)
                                {
-                                       attrdef = (AttrDefault *) palloc(relation->rd_rel->relnatts *
-                                                                                                        sizeof(AttrDefault));
+                                       attrdef = (AttrDefault *)
+                                               MemoryContextAlloc(CacheMemoryContext,
+                                                                                  relation->rd_rel->relnatts *
+                                                                                  sizeof(AttrDefault));
                                        MemSet(attrdef, 0,
-                                          relation->rd_rel->relnatts * sizeof(AttrDefault));
+                                                  relation->rd_rel->relnatts * sizeof(AttrDefault));
                                }
                                attrdef[ndef].adnum = attp->attnum;
                                attrdef[ndef].adbin = NULL;
@@ -575,17 +592,18 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo,
 
 static void
 build_tupdesc_ind(RelationBuildDescInfo buildinfo,
-                                 Relation relation,
-                                 u_int natts)
+                                 Relation relation)
 {
        Relation        attrel;
        HeapTuple       atttup;
        Form_pg_attribute attp;
-       TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+       TupleConstr *constr;
        AttrDefault *attrdef = NULL;
        int                     ndef = 0;
        int                     i;
 
+       constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
+                                                                                               sizeof(TupleConstr));
        constr->has_not_null = false;
 
        attrel = heap_openr(AttributeRelationName, AccessShareLock);
@@ -616,7 +634,8 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
                }
 
                relation->rd_att->attrs[i - 1] = attp =
-                       (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
+                       (Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                  ATTRIBUTE_TUPLE_SIZE);
 
                memcpy((char *) attp,
                           (char *) (Form_pg_attribute) GETSTRUCT(atttup),
@@ -638,8 +657,10 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
                {
                        if (attrdef == NULL)
                        {
-                               attrdef = (AttrDefault *) palloc(relation->rd_rel->relnatts *
-                                                                                                sizeof(AttrDefault));
+                               attrdef = (AttrDefault *)
+                                       MemoryContextAlloc(CacheMemoryContext,
+                                                                          relation->rd_rel->relnatts *
+                                                                          sizeof(AttrDefault));
                                MemSet(attrdef, 0,
                                           relation->rd_rel->relnatts * sizeof(AttrDefault));
                        }
@@ -652,7 +673,6 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
        heap_close(attrel, AccessShareLock);
 
        SetConstrOfRelation(relation, constr, ndef, attrdef);
-
 }
 
 /* --------------------------------
@@ -660,11 +680,22 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
  *
  *             Form the relation's rewrite rules from information in
  *             the pg_rewrite system catalog.
+ *
+ * Note: The rule parsetrees are potentially very complex node structures.
+ * To allow these trees to be freed when the relcache entry is flushed,
+ * we make a private memory context to hold the RuleLock information for
+ * each relcache entry that has associated rules.  The context is used
+ * just for rule info, not for any other subsidiary data of the relcache
+ * entry, because that keeps the update logic in RelationClearRelation()
+ * manageable.  The other subsidiary data structures are simple enough
+ * to be easy to free explicitly, anyway.
  * --------------------------------
  */
 static void
 RelationBuildRuleLock(Relation relation)
 {
+       MemoryContext rulescxt;
+       MemoryContext oldcxt;
        HeapTuple       pg_rewrite_tuple;
        Relation        pg_rewrite_desc;
        TupleDesc       pg_rewrite_tupdesc;
@@ -675,13 +706,25 @@ RelationBuildRuleLock(Relation relation)
        RewriteRule **rules;
        int                     maxlocks;
 
+       /*
+        * Make the private context.  Parameters are set on the assumption
+        * that it'll probably not contain much data.
+        */
+       rulescxt = AllocSetContextCreate(CacheMemoryContext,
+                                                                        RelationGetRelationName(relation),
+                                                                        0,     /* minsize */
+                                                                        1024, /* initsize */
+                                                                        1024); /* maxsize */
+       relation->rd_rulescxt = rulescxt;
+
        /* ----------------
         *      form an array to hold the rewrite rules (the array is extended if
         *      necessary)
         * ----------------
         */
        maxlocks = 4;
-       rules = (RewriteRule **) palloc(sizeof(RewriteRule *) * maxlocks);
+       rules = (RewriteRule **)
+               MemoryContextAlloc(rulescxt, sizeof(RewriteRule *) * maxlocks);
        numlocks = 0;
 
        /* ----------------
@@ -710,26 +753,32 @@ RelationBuildRuleLock(Relation relation)
                char       *rule_evqual_str;
                RewriteRule *rule;
 
-               rule = (RewriteRule *) palloc(sizeof(RewriteRule));
+               rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
+                                                                                                 sizeof(RewriteRule));
 
                rule->ruleId = pg_rewrite_tuple->t_data->t_oid;
 
-               rule->event = (int) heap_getattr(pg_rewrite_tuple,
-                                                        Anum_pg_rewrite_ev_type, pg_rewrite_tupdesc,
-                                                                                &isnull) - 48;
-               rule->attrno = (int) heap_getattr(pg_rewrite_tuple,
-                                                        Anum_pg_rewrite_ev_attr, pg_rewrite_tupdesc,
-                                                                                 &isnull);
-               rule->isInstead = !!heap_getattr(pg_rewrite_tuple,
-                                                 Anum_pg_rewrite_is_instead, pg_rewrite_tupdesc,
-                                                                                &isnull);
+               rule->event = DatumGetInt32(heap_getattr(pg_rewrite_tuple,
+                                                                                                Anum_pg_rewrite_ev_type,
+                                                                                                pg_rewrite_tupdesc,
+                                                                                                &isnull)) - 48;
+               rule->attrno = DatumGetInt16(heap_getattr(pg_rewrite_tuple,
+                                                                                                 Anum_pg_rewrite_ev_attr,
+                                                                                                 pg_rewrite_tupdesc,
+                                                                                                 &isnull));
+               rule->isInstead = DatumGetBool(heap_getattr(pg_rewrite_tuple,
+                                                                                                       Anum_pg_rewrite_is_instead,
+                                                                                                       pg_rewrite_tupdesc,
+                                                                                                       &isnull));
 
                ruleaction = heap_getattr(pg_rewrite_tuple,
                                                                  Anum_pg_rewrite_ev_action,
                                                                  pg_rewrite_tupdesc,
                                                                  &isnull);
                ruleaction_str = lztextout((lztext *) DatumGetPointer(ruleaction));
+               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
                rule->actions = (List *) stringToNode(ruleaction_str);
+               MemoryContextSwitchTo(oldcxt);
                pfree(ruleaction_str);
 
                rule_evqual = heap_getattr(pg_rewrite_tuple,
@@ -737,13 +786,16 @@ RelationBuildRuleLock(Relation relation)
                                                                   pg_rewrite_tupdesc,
                                                                   &isnull);
                rule_evqual_str = lztextout((lztext *) DatumGetPointer(rule_evqual));
+               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
                rule->qual = (Node *) stringToNode(rule_evqual_str);
+               MemoryContextSwitchTo(oldcxt);
                pfree(rule_evqual_str);
 
                if (numlocks >= maxlocks)
                {
                        maxlocks *= 2;
-                       rules = (RewriteRule **) repalloc(rules, sizeof(RewriteRule *) * maxlocks);
+                       rules = (RewriteRule **)
+                               repalloc(rules, sizeof(RewriteRule *) * maxlocks);
                }
                rules[numlocks++] = rule;
        }
@@ -759,42 +811,13 @@ RelationBuildRuleLock(Relation relation)
         *      form a RuleLock and insert into relation
         * ----------------
         */
-       rulelock = (RuleLock *) palloc(sizeof(RuleLock));
+       rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
        rulelock->numLocks = numlocks;
        rulelock->rules = rules;
 
        relation->rd_rules = rulelock;
 }
 
-/* --------------------------------
- *             FreeRuleLock
- *
- *             Release the storage used for a set of rewrite rules.
- *
- *             Probably this should be in the rules code someplace...
- * --------------------------------
- */
-static void
-FreeRuleLock(RuleLock *rlock)
-{
-       int                     i;
-
-       if (rlock == NULL)
-               return;
-       for (i = 0; i < rlock->numLocks; i++)
-       {
-               RewriteRule *rule = rlock->rules[i];
-
-#if 0                                                  /* does freefuncs.c still work?  Not sure */
-               freeObject(rule->actions);
-               freeObject(rule->qual);
-#endif
-               pfree(rule);
-       }
-       pfree(rlock->rules);
-       pfree(rlock);
-}
-
 /* --------------------------------
  *             equalRuleLocks
  *
@@ -886,7 +909,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 {
        File            fd;
        Relation        relation;
-       u_int           natts;
        Oid                     relid;
        Oid                     relam;
        HeapTuple       pg_class_tuple;
@@ -912,17 +934,19 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
         */
        relid = pg_class_tuple->t_data->t_oid;
        relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
-       natts = relp->relnatts;
 
        /* ----------------
         *      allocate storage for the relation descriptor,
-        *      initialize relation->rd_rel and get the access method id.
-        *      The storage is allocated in memory context CacheMemoryContext.
+        *      and copy pg_class_tuple to relation->rd_rel.
         * ----------------
         */
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-       relation = AllocateRelationDesc(oldrelation, natts, relp);
-       relam = relation->rd_rel->relam;
+       relation = AllocateRelationDesc(oldrelation, relp);
+
+       /* -------------------
+        *      now we can free the memory allocated for pg_class_tuple
+        * -------------------
+        */
+       heap_freetuple(pg_class_tuple);
 
        /* ----------------
         *      initialize the relation's relation id (relation->rd_id)
@@ -946,26 +970,30 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
         *      initialize the access method information (relation->rd_am)
         * ----------------
         */
+       relam = relation->rd_rel->relam;
        if (OidIsValid(relam))
-               relation->rd_am = AccessMethodObjectIdGetForm(relam);
+               relation->rd_am = AccessMethodObjectIdGetForm(relam,
+                                                                                                         CacheMemoryContext);
 
        /* ----------------
         *      initialize the tuple descriptor (relation->rd_att).
         * ----------------
         */
-       RelationBuildTupleDesc(buildinfo, relation, natts);
+       RelationBuildTupleDesc(buildinfo, relation);
 
        /* ----------------
-        *      initialize rules that affect this relation
+        *      Fetch rules and triggers that affect this relation
         * ----------------
         */
-       if (relp->relhasrules)
+       if (relation->rd_rel->relhasrules)
                RelationBuildRuleLock(relation);
        else
+       {
                relation->rd_rules = NULL;
+               relation->rd_rulescxt = NULL;
+       }
 
-       /* Triggers */
-       if (relp->reltriggers > 0)
+       if (relation->rd_rel->reltriggers > 0)
                RelationBuildTriggers(relation);
        else
                relation->trigdesc = NULL;
@@ -993,7 +1021,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
        Assert(fd >= -1);
        if (fd == -1)
                elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m",
-                        NameStr(relp->relname));
+                        NameStr(relation->rd_rel->relname));
 
        relation->rd_fd = fd;
 
@@ -1002,17 +1030,10 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
         *      restore memory context and return the new reldesc.
         * ----------------
         */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
        RelationCacheInsert(relation);
-
        MemoryContextSwitchTo(oldcxt);
 
-       /* -------------------
-        *      free the memory allocated for pg_class_tuple
-        *      and for lock data pointed to by pg_class_tuple
-        * -------------------
-        */
-       heap_freetuple(pg_class_tuple);
-
        return relation;
 }
 
@@ -1030,13 +1051,15 @@ IndexedAccessMethodInitialize(Relation relation)
        natts = relation->rd_rel->relnatts;
        relamstrategies = relation->rd_am->amstrategies;
        stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies);
-       strategy = (IndexStrategy) palloc(stratSize);
-       relamsupport = relation->rd_am->amsupport;
+       strategy = (IndexStrategy) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                 stratSize);
 
+       relamsupport = relation->rd_am->amsupport;
        if (relamsupport > 0)
        {
                supportSize = natts * (relamsupport * sizeof(RegProcedure));
-               support = (RegProcedure *) palloc(supportSize);
+               support = (RegProcedure *) MemoryContextAlloc(CacheMemoryContext,
+                                                                                                         supportSize);
        }
        else
                support = (RegProcedure *) NULL;
@@ -1052,20 +1075,20 @@ IndexedAccessMethodInitialize(Relation relation)
 /* --------------------------------
  *             formrdesc
  *
- *             This is a special version of RelationBuildDesc()
- *             used by RelationInitialize() in initializing the
- *             relcache.  The system relation descriptors built
- *             here are all nailed in the descriptor caches, for
- *             bootstrapping.
+ *             This is a special cut-down version of RelationBuildDesc()
+ *             used by RelationInitialize() in initializing the relcache.
+ *             The relation descriptor is built just from the supplied parameters.
+ *
+ * NOTE: we assume we are already switched into CacheMemoryContext.
  * --------------------------------
  */
 static void
 formrdesc(char *relationName,
-                 u_int natts,
+                 int natts,
                  FormData_pg_attribute *att)
 {
        Relation        relation;
-       u_int           i;
+       int                     i;
 
        /* ----------------
         *      allocate new relation desc
@@ -1095,8 +1118,9 @@ formrdesc(char *relationName,
        strcpy(RelationGetPhysicalRelationName(relation), relationName);
 
        /* ----------------
-          initialize attribute tuple form
-       */
+        *      initialize attribute tuple form
+        * ----------------
+        */
        relation->rd_att = CreateTemplateTupleDesc(natts);
 
        /*
@@ -1120,7 +1144,7 @@ formrdesc(char *relationName,
        relation->rd_rel->relpages = 1;         /* XXX */
        relation->rd_rel->reltuples = 1;        /* XXX */
        relation->rd_rel->relkind = RELKIND_RELATION;
-       relation->rd_rel->relnatts = (uint16) natts;
+       relation->rd_rel->relnatts = (int16) natts;
        relation->rd_isnailed = true;
 
        /* ----------------
@@ -1157,6 +1181,10 @@ formrdesc(char *relationName,
         * Determining this requires a scan on pg_class, but to do the scan
         * the rdesc for pg_class must already exist.  Therefore we must do
         * the check (and possible set) after cache insertion.
+        *
+        * XXX I believe the above comment is misguided; we should be
+        * running in bootstrap or init processing mode, and CatalogHasIndex
+        * relies on hard-wired info in those cases.
         */
        relation->rd_rel->relhasindex =
                CatalogHasIndex(relationName, RelationGetRelid(relation));
@@ -1171,8 +1199,9 @@ formrdesc(char *relationName,
 /* --------------------------------
  *             RelationIdCacheGetRelation
  *
- *             Lookup a reldesc by OID.
- *             Only try to get the reldesc by looking up the cache
+ *             Lookup an existing reldesc by OID.
+ *
+ *             Only try to get the reldesc by looking in the cache,
  *             do not go to the disk.
  *
  *             NB: relation ref count is incremented if successful.
@@ -1355,7 +1384,7 @@ RelationClose(Relation relation)
  *      (one with refcount > 0).  However, this routine just does whichever
  *      it's told to do; callers must determine which they want.
  *
- *      If we detect a change in the relation's TupleDesc or trigger data
+ *      If we detect a change in the relation's TupleDesc, rules, or triggers
  *      while rebuilding, we complain unless refcount is 0.
  * --------------------------------
  */
@@ -1383,8 +1412,6 @@ RelationClearRelation(Relation relation, bool rebuildIt)
        if (relation->rd_isnailed)
                return;
 
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
        /*
         * Remove relation from hash tables
         *
@@ -1392,7 +1419,9 @@ RelationClearRelation(Relation relation, bool rebuildIt)
         * visible in the hash tables until it's valid again, so don't try to
         * optimize this away...
         */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
        RelationCacheDelete(relation);
+       MemoryContextSwitchTo(oldcxt);
 
        /* Clear out catcache's entries for this relation */
        SystemCacheRelationFlushed(RelationGetRelid(relation));
@@ -1425,7 +1454,8 @@ RelationClearRelation(Relation relation, bool rebuildIt)
        {
                /* ok to zap remaining substructure */
                FreeTupleDesc(relation->rd_att);
-               FreeRuleLock(relation->rd_rules);
+               if (relation->rd_rulescxt)
+                       MemoryContextDelete(relation->rd_rulescxt);
                FreeTriggerDesc(relation->trigdesc);
                pfree(relation);
        }
@@ -1443,6 +1473,7 @@ RelationClearRelation(Relation relation, bool rebuildIt)
                bool            old_myxactonly = relation->rd_myxactonly;
                TupleDesc       old_att = relation->rd_att;
                RuleLock   *old_rules = relation->rd_rules;
+               MemoryContext old_rulescxt = relation->rd_rulescxt;
                TriggerDesc *old_trigdesc = relation->trigdesc;
                int                     old_nblocks = relation->rd_nblocks;
                bool            relDescChanged = false;
@@ -1455,7 +1486,8 @@ RelationClearRelation(Relation relation, bool rebuildIt)
                {
                        /* Should only get here if relation was deleted */
                        FreeTupleDesc(old_att);
-                       FreeRuleLock(old_rules);
+                       if (old_rulescxt)
+                               MemoryContextDelete(old_rulescxt);
                        FreeTriggerDesc(old_trigdesc);
                        pfree(relation);
                        elog(ERROR, "RelationClearRelation: relation %u deleted while still in use",
@@ -1475,12 +1507,15 @@ RelationClearRelation(Relation relation, bool rebuildIt)
                }
                if (equalRuleLocks(old_rules, relation->rd_rules))
                {
-                       FreeRuleLock(relation->rd_rules);
+                       if (relation->rd_rulescxt)
+                               MemoryContextDelete(relation->rd_rulescxt);
                        relation->rd_rules = old_rules;
+                       relation->rd_rulescxt = old_rulescxt;
                }
                else
                {
-                       FreeRuleLock(old_rules);
+                       if (old_rulescxt)
+                               MemoryContextDelete(old_rulescxt);
                        relDescChanged = true;
                }
                if (equalTriggerDescs(old_trigdesc, relation->trigdesc))
@@ -1505,8 +1540,6 @@ RelationClearRelation(Relation relation, bool rebuildIt)
                        elog(ERROR, "RelationClearRelation: relation %u modified while in use",
                                 buildinfo.i.info_id);
        }
-
-       MemoryContextSwitchTo(oldcxt);
 }
 
 /* --------------------------------
@@ -1570,12 +1603,9 @@ RelationForgetRelation(Oid rid)
        {
                if (relation->rd_myxactonly)
                {
-                       MemoryContext oldcxt;
                        List       *curr;
                        List       *prev = NIL;
 
-                       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
                        foreach(curr, newlyCreatedRelns)
                        {
                                Relation        reln = lfirst(curr);
@@ -1593,7 +1623,6 @@ RelationForgetRelation(Oid rid)
                        else
                                lnext(prev) = lnext(curr);
                        pfree(curr);
-                       MemoryContextSwitchTo(oldcxt);
                }
 
                /* Unconditionally destroy the relcache entry */
@@ -1731,10 +1760,10 @@ RelationRegisterRelation(Relation relation)
 {
        MemoryContext oldcxt;
 
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
        RelationInitLockInfo(relation);
 
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
        RelationCacheInsert(relation);
 
        /*
@@ -1761,13 +1790,9 @@ RelationRegisterRelation(Relation relation)
 void
 RelationPurgeLocalRelation(bool xactCommitted)
 {
-       MemoryContext oldcxt;
-
        if (newlyCreatedRelns == NULL)
                return;
 
-       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-
        while (newlyCreatedRelns)
        {
                List       *l = newlyCreatedRelns;
@@ -1796,8 +1821,6 @@ RelationPurgeLocalRelation(bool xactCommitted)
                if (!IsBootstrapProcessingMode())
                        RelationClearRelation(reln, false);
        }
-
-       MemoryContextSwitchTo(oldcxt);
 }
 
 /* --------------------------------
@@ -1943,7 +1966,9 @@ AttrDefaultFetch(Relation relation)
                                elog(NOTICE, "AttrDefaultFetch: adbin IS NULL for attr %s in rel %s",
                                         NameStr(relation->rd_att->attrs[adform->adnum - 1]->attname),
                                         RelationGetRelationName(relation));
-                       attrdef[i].adbin = textout(val);
+                       else
+                               attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext,
+                                                                                                          textout(val));
                        break;
                }
                if (hasindex)
@@ -2039,14 +2064,16 @@ RelCheckFetch(Relation relation)
                if (isnull)
                        elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
                                 RelationGetRelationName(relation));
-               check[found].ccname = pstrdup(NameStr(*rcname));
+               check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
+                                                                                                 NameStr(*rcname));
                val = (struct varlena *) fastgetattr(htup,
                                                                                         Anum_pg_relcheck_rcbin,
                                                                                         rcrel->rd_att, &isnull);
                if (isnull)
                        elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
                                 RelationGetRelationName(relation));
-               check[found].ccbin = textout(val);
+               check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
+                                                                                                textout(val));
                found++;
                if (hasindex)
                        ReleaseBuffer(buffer);
@@ -2416,7 +2443,7 @@ init_irels(void)
 
                RelationCacheInsert(ird);
        }
-       criticalRelcacheBuild = true;
+       criticalRelcachesBuilt = true;
 }
 
 static void
@@ -2489,7 +2516,7 @@ write_irels(void)
        irel[2] = RelationBuildDesc(bi, NULL);
        irel[2]->rd_isnailed = true;
 
-       criticalRelcacheBuild = true;
+       criticalRelcachesBuilt = true;
 
        /*
         * Removed the following ProcessingMode -- inoue
index 15cfb21e21e83b35dea8ad8ad5147da6a156d316..6676265d66613dd7ad0bd4d51da5b9abae8c873a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: index.h,v 1.25 2000/06/17 23:41:51 tgl Exp $
+ * $Id: index.h,v 1.26 2000/06/30 07:04:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,8 @@
 #include "access/itup.h"
 #include "nodes/execnodes.h"
 
-extern Form_pg_am AccessMethodObjectIdGetForm(Oid accessMethodObjectId);
+extern Form_pg_am AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
+                                                                                         MemoryContext resultCxt);
 
 extern void UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate);
 
index 25541ed9b9566e64444bbae30b369f99b2350a1a..589f4245f740ff49abf4ce612f31a955da5307da 100644 (file)
@@ -7,22 +7,15 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rewriteSupport.h,v 1.11 2000/01/26 05:58:30 momjian Exp $
+ * $Id: rewriteSupport.h,v 1.12 2000/06/30 07:04:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef REWRITESUPPORT_H
 #define REWRITESUPPORT_H
 
-#include "access/attnum.h"
-#include "nodes/pg_list.h"
-
 extern int     IsDefinedRewriteRule(char *ruleName);
 
-extern void prs2_addToRelation(Oid relid, Oid ruleId, CmdType event_type,
-                                  AttrNumber attno, bool isInstead, Node *qual,
-                                  List *actions);
-extern void prs2_deleteFromRelation(Oid relid, Oid ruleId);
-
+extern void setRelhasrulesInRelation(Oid relationId, bool relhasrules);
 
 #endif  /* REWRITESUPPORT_H */
index 901020611aca5547708c18ca58c73668a1d5dffd..121012d7fd4984bf61683ec5fcfec04e9bd8a2f5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.38 2000/06/18 22:44:34 tgl Exp $
+ * $Id: rel.h,v 1.39 2000/06/30 07:04:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -85,7 +85,7 @@ typedef struct TriggerDesc
 
 typedef struct RelationData
 {
-       File            rd_fd;                  /* open file descriptor */
+       File            rd_fd;                  /* open file descriptor, or -1 if none */
        int                     rd_nblocks;             /* number of blocks in rel */
        uint16          rd_refcnt;              /* reference count */
        bool            rd_myxactonly;  /* rel uses the local buffer mgr */
@@ -99,6 +99,7 @@ typedef struct RelationData
        LockInfoData rd_lockInfo;       /* lock mgr's info for locking relation */
        TupleDesc       rd_att;                 /* tuple descriptor */
        RuleLock   *rd_rules;           /* rewrite rules */
+       MemoryContext rd_rulescxt;      /* private memory cxt for rd_rules, if any */
        IndexStrategy rd_istrat;        /* info needed if rel is an index */
        RegProcedure *rd_support;
        TriggerDesc *trigdesc;          /* Trigger info, or NULL if rel has none */