From b41f4ab8c448fc1bb13b52d00c9c4f1868d97941 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 30 Jun 2000 07:04:23 +0000 Subject: [PATCH] Use a private memory context to store rule information in each relcache 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 | 39 ++- src/backend/commands/trigger.c | 48 ++-- src/backend/rewrite/rewriteDefine.c | 123 ++++------ src/backend/rewrite/rewriteHandler.c | 12 +- src/backend/rewrite/rewriteRemove.c | 73 +++--- src/backend/rewrite/rewriteSupport.c | 196 ++------------- src/backend/utils/cache/relcache.c | 351 ++++++++++++++------------- src/include/catalog/index.h | 5 +- src/include/rewrite/rewriteSupport.h | 11 +- src/include/utils/rel.h | 5 +- 10 files changed, 346 insertions(+), 517 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index c737618996..2c5ff64dab 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -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 diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index fbb5a694b8..075f3a508b 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -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; diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 08351fe669..d92190f652 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -29,27 +29,16 @@ * 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); } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 255190ebf5..acd62c9c70 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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", diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index 13a07adbd8..d50e104909 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -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); } /* diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c index 113e0b73c5..fe87f56938 100644 --- a/src/backend/rewrite/rewriteSupport.c +++ b/src/backend/rewrite/rewriteSupport.c @@ -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" @@ -22,60 +21,6 @@ #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); -} diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index b0ee20fce9..dc69a0e508 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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,11 +381,30 @@ 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; + /* 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 all fields of reldesc + * ---------------- + */ + MemSet((char *) relation, 0, sizeof(RelationData)); + + /* make sure relation is marked as having no open file yet */ + relation->rd_fd = -1; + /* ---------------- * Copy the relation tuple form * @@ -399,27 +420,14 @@ AllocateRelationDesc(Relation relation, u_int natts, memcpy((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE); - /* ---------------- - * allocate space for new relation descriptor, if needed - */ - if (relation == NULL) - relation = (Relation) palloc(sizeof(RelationData)); - - /* ---------------- - * clear new reldesc - * ---------------- - */ - MemSet((char *) relation, 0, sizeof(RelationData)); - - /* make sure relation is marked as having no open file yet */ - relation->rd_fd = -1; - - /* initialize attribute tuple form */ - relation->rd_att = CreateTemplateTupleDesc(natts); - - /* and initialize relation tuple form */ + /* 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 diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 15cfb21e21..6676265d66 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -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); diff --git a/src/include/rewrite/rewriteSupport.h b/src/include/rewrite/rewriteSupport.h index 25541ed9b9..589f4245f7 100644 --- a/src/include/rewrite/rewriteSupport.h +++ b/src/include/rewrite/rewriteSupport.h @@ -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 */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 901020611a..121012d7fd 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -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 */