mirror of
https://github.com/postgres/postgres.git
synced 2025-06-26 12:21:12 +03:00
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.
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user