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 + -
显示快捷键?