plancache.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,016 行 · 第 1/2 页

C
1,016
字号
			if (plansource->resultDesc)				FreeTupleDesc(plansource->resultDesc);			plansource->resultDesc = resultDesc;			MemoryContextSwitchTo(oldcxt);		}		/* Now we can restore current search path */		PopOverrideSearchPath();		/*		 * Store the plans into the plancache entry, advancing the generation		 * count.		 */		StoreCachedPlan(plansource, slist, NULL);		plan = plansource->plan;	}	/*	 * Last step: flag the plan as in use by caller.	 */	if (useResOwner)		ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner);	plan->refcount++;	if (useResOwner)		ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan);	return plan;}/* * Invoke the planner on some rewritten queries.  This is broken out of * RevalidateCachedPlan just to avoid plastering "volatile" all over that * function's variables. */static List *do_planning(List *querytrees, int cursorOptions){	List	   *stmt_list;	/*	 * If a snapshot is already set (the normal case), we can just use that	 * for planning.  But if it isn't, we have to tell pg_plan_queries to make	 * a snap if it needs one.	In that case we should arrange to reset	 * ActiveSnapshot afterward, to ensure that RevalidateCachedPlan has no	 * caller-visible effects on the snapshot.	Having to replan is an unusual	 * case, and it seems a really bad idea for RevalidateCachedPlan to affect	 * the snapshot only in unusual cases.	(Besides, the snap might have been	 * created in a short-lived context.)	 */	if (ActiveSnapshot != NULL)		stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, false);	else	{		PG_TRY();		{			stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, true);		}		PG_CATCH();		{			/* Restore global vars and propagate error */			ActiveSnapshot = NULL;			PG_RE_THROW();		}		PG_END_TRY();		ActiveSnapshot = NULL;	}	return stmt_list;}/* * ReleaseCachedPlan: release active use of a cached plan. * * This decrements the reference count, and frees the plan if the count * has thereby gone to zero.  If useResOwner is true, it is assumed that * the reference count is managed by the CurrentResourceOwner. * * Note: useResOwner = false is used for releasing references that are in * persistent data structures, such as the parent CachedPlanSource or a * Portal.	Transient references should be protected by a resource owner. */voidReleaseCachedPlan(CachedPlan *plan, bool useResOwner){	if (useResOwner)		ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan);	Assert(plan->refcount > 0);	plan->refcount--;	if (plan->refcount == 0)		MemoryContextDelete(plan->context);}/* * CachedPlanIsValid: test whether the plan within a CachedPlanSource is * currently valid (that is, not marked as being in need of revalidation). * * This result is only trustworthy (ie, free from race conditions) if * the caller has acquired locks on all the relations used in the plan. */boolCachedPlanIsValid(CachedPlanSource *plansource){	CachedPlan *plan;	/* Validity check that we were given a CachedPlanSource */	Assert(list_member_ptr(cached_plans_list, plansource));	plan = plansource->plan;	if (plan && !plan->dead)	{		/*		 * Plan must have positive refcount because it is referenced by		 * plansource; so no need to fear it disappears under us here.		 */		Assert(plan->refcount > 0);		/*		 * Although we don't want to acquire locks here, it still seems		 * useful to check for expiration of a transient plan.		 */		if (TransactionIdIsValid(plan->saved_xmin) &&			!TransactionIdEquals(plan->saved_xmin, TransactionXmin))			plan->dead = true;		else			return true;	}	return false;}/* * AcquireExecutorLocks: acquire locks needed for execution of a fully-planned * cached plan; or release them if acquire is false. */static voidAcquireExecutorLocks(List *stmt_list, bool acquire){	ListCell   *lc1;	foreach(lc1, stmt_list)	{		PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1);		int			rt_index;		ListCell   *lc2;		Assert(!IsA(plannedstmt, Query));		if (!IsA(plannedstmt, PlannedStmt))			continue;			/* Ignore utility statements */		rt_index = 0;		foreach(lc2, plannedstmt->rtable)		{			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);			LOCKMODE	lockmode;			rt_index++;			if (rte->rtekind != RTE_RELATION)				continue;			/*			 * Acquire the appropriate type of lock on each relation OID. Note			 * that we don't actually try to open the rel, and hence will not			 * fail if it's been dropped entirely --- we'll just transiently			 * acquire a non-conflicting lock.			 */			if (list_member_int(plannedstmt->resultRelations, rt_index))				lockmode = RowExclusiveLock;			else if (rowmark_member(plannedstmt->rowMarks, rt_index))				lockmode = RowShareLock;			else				lockmode = AccessShareLock;			if (acquire)				LockRelationOid(rte->relid, lockmode);			else				UnlockRelationOid(rte->relid, lockmode);		}	}}/* * AcquirePlannerLocks: acquire locks needed for planning and execution of a * not-fully-planned cached plan; or release them if acquire is false. * * Note that we don't actually try to open the relations, and hence will not * fail if one has been dropped entirely --- we'll just transiently acquire * a non-conflicting lock. */static voidAcquirePlannerLocks(List *stmt_list, bool acquire){	ListCell   *lc;	foreach(lc, stmt_list)	{		Query	   *query = (Query *) lfirst(lc);		Assert(IsA(query, Query));		if (acquire)			ScanQueryForRelids(query, LockRelid, NULL);		else			ScanQueryForRelids(query, UnlockRelid, NULL);	}}/* * ScanQueryForRelids callback functions for AcquirePlannerLocks */static voidLockRelid(Oid relid, LOCKMODE lockmode, void *arg){	LockRelationOid(relid, lockmode);}static voidUnlockRelid(Oid relid, LOCKMODE lockmode, void *arg){	UnlockRelationOid(relid, lockmode);}/* * ScanQueryForRelids: recursively scan one Query and apply the callback * function to each relation OID found therein.  The callback function * takes the arguments relation OID, lockmode, pointer arg. */static voidScanQueryForRelids(Query *parsetree,				   void (*callback) (),				   void *arg){	ListCell   *lc;	int			rt_index;	/*	 * First, process RTEs of the current query level.	 */	rt_index = 0;	foreach(lc, parsetree->rtable)	{		RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);		LOCKMODE	lockmode;		rt_index++;		switch (rte->rtekind)		{			case RTE_RELATION:				/*				 * Determine the lock type required for this RTE.				 */				if (rt_index == parsetree->resultRelation)					lockmode = RowExclusiveLock;				else if (rowmark_member(parsetree->rowMarks, rt_index))					lockmode = RowShareLock;				else					lockmode = AccessShareLock;				(*callback) (rte->relid, lockmode, arg);				break;			case RTE_SUBQUERY:				/*				 * The subquery RTE itself is all right, but we have to				 * recurse to process the represented subquery.				 */				ScanQueryForRelids(rte->subquery, callback, arg);				break;			default:				/* ignore other types of RTEs */				break;		}	}	/*	 * Recurse into sublink subqueries, too.  But we already did the ones in	 * the rtable.	 */	if (parsetree->hasSubLinks)	{		ScanQueryWalkerContext context;		context.callback = callback;		context.arg = arg;		query_tree_walker(parsetree, ScanQueryWalker,						  (void *) &context,						  QTW_IGNORE_RT_SUBQUERIES);	}}/* * Walker to find sublink subqueries for ScanQueryForRelids */static boolScanQueryWalker(Node *node, ScanQueryWalkerContext *context){	if (node == NULL)		return false;	if (IsA(node, SubLink))	{		SubLink    *sub = (SubLink *) node;		/* Do what we came for */		ScanQueryForRelids((Query *) sub->subselect,						   context->callback, context->arg);		/* Fall through to process lefthand args of SubLink */	}	/*	 * Do NOT recurse into Query nodes, because ScanQueryForRelids already	 * processed subselects of subselects for us.	 */	return expression_tree_walker(node, ScanQueryWalker,								  (void *) context);}/* * rowmark_member: check whether an RT index appears in a RowMarkClause list. */static boolrowmark_member(List *rowMarks, int rt_index){	ListCell   *l;	foreach(l, rowMarks)	{		RowMarkClause *rc = (RowMarkClause *) lfirst(l);		if (rc->rti == rt_index)			return true;	}	return false;}/* * plan_list_is_transient: check if any of the plans in the list are transient. */static boolplan_list_is_transient(List *stmt_list){	ListCell   *lc;	foreach(lc, stmt_list)	{		PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);		if (!IsA(plannedstmt, PlannedStmt))			continue;			/* Ignore utility statements */		if (plannedstmt->transientPlan)			return true;	}	return false;}/* * PlanCacheComputeResultDesc: given a list of either fully-planned statements * or Queries, determine the result tupledesc it will produce.	Returns NULL * if the execution will not return tuples. * * Note: the result is created or copied into current memory context. */TupleDescPlanCacheComputeResultDesc(List *stmt_list){	Node	   *node;	Query	   *query;	PlannedStmt *pstmt;	switch (ChoosePortalStrategy(stmt_list))	{		case PORTAL_ONE_SELECT:			node = (Node *) linitial(stmt_list);			if (IsA(node, Query))			{				query = (Query *) node;				return ExecCleanTypeFromTL(query->targetList, false);			}			if (IsA(node, PlannedStmt))			{				pstmt = (PlannedStmt *) node;				return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);			}			/* other cases shouldn't happen, but return NULL */			break;		case PORTAL_ONE_RETURNING:			node = PortalListGetPrimaryStmt(stmt_list);			if (IsA(node, Query))			{				query = (Query *) node;				Assert(query->returningList);				return ExecCleanTypeFromTL(query->returningList, false);			}			if (IsA(node, PlannedStmt))			{				pstmt = (PlannedStmt *) node;				Assert(pstmt->returningLists);				return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);			}			/* other cases shouldn't happen, but return NULL */			break;		case PORTAL_UTIL_SELECT:			node = (Node *) linitial(stmt_list);			if (IsA(node, Query))			{				query = (Query *) node;				Assert(query->utilityStmt);				return UtilityTupleDescriptor(query->utilityStmt);			}			/* else it's a bare utility statement */			return UtilityTupleDescriptor(node);		case PORTAL_MULTI_QUERY:			/* will not return tuples */			break;	}	return NULL;}/* * PlanCacheCallback *		Relcache inval callback function * * Invalidate all plans mentioning the given rel, or all plans mentioning * any rel at all if relid == InvalidOid. */static voidPlanCacheCallback(Datum arg, Oid relid){	ListCell   *lc1;	ListCell   *lc2;	foreach(lc1, cached_plans_list)	{		CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);		CachedPlan *plan = plansource->plan;		/* No work if it's already invalidated */		if (!plan || plan->dead)			continue;		if (plan->fully_planned)		{			foreach(lc2, plan->stmt_list)			{				PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);				Assert(!IsA(plannedstmt, Query));				if (!IsA(plannedstmt, PlannedStmt))					continue;	/* Ignore utility statements */				if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :					list_member_oid(plannedstmt->relationOids, relid))				{					/* Invalidate the plan! */					plan->dead = true;					break;		/* out of stmt_list scan */				}			}		}		else		{			/*			 * For not-fully-planned entries we use ScanQueryForRelids, since			 * a recursive traversal is needed.  The callback API is a bit			 * tedious but avoids duplication of coding.			 */			InvalRelidContext context;			context.inval_relid = relid;			context.plan = plan;			foreach(lc2, plan->stmt_list)			{				Query	   *query = (Query *) lfirst(lc2);				Assert(IsA(query, Query));				ScanQueryForRelids(query, InvalRelid, (void *) &context);			}		}	}}/* * ResetPlanCache: drop all cached plans. */voidResetPlanCache(void){	PlanCacheCallback((Datum) 0, InvalidOid);}/* * ScanQueryForRelids callback function for PlanCacheCallback */static voidInvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context){	if (relid == context->inval_relid || context->inval_relid == InvalidOid)		context->plan->dead = true;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?