diff options
author | Tom Lane | 2000-06-30 07:04:23 +0000 |
---|---|---|
committer | Tom Lane | 2000-06-30 07:04:23 +0000 |
commit | b41f4ab8c448fc1bb13b52d00c9c4f1868d97941 (patch) | |
tree | 5e832a1210f93033aeab63a572dc81229ad4aa7c /src/backend/rewrite | |
parent | c9ec78a6b89e846c5b1be0aa40b6532ca209c850 (diff) |
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.
Diffstat (limited to 'src/backend/rewrite')
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 123 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 12 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteRemove.c | 73 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteSupport.c | 196 |
4 files changed, 101 insertions, 303 deletions
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 08351fe6696..d92190f6524 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 255190ebf52..acd62c9c70e 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 13a07adbd8c..d50e1049097 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 113e0b73c59..fe87f569387 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); -} |