static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel) { ResourceOwner child; ResourceOwner save; ResourceReleaseCallbackItem *item; /* Recurse to handle descendants */ for (child = owner->firstchild; child != NULL; child = child->nextchild) ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel); /* * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't * get confused. We needn't PG_TRY here because the outermost level will * fix it on error abort. */ save = CurrentResourceOwner; CurrentResourceOwner = owner; if (phase == RESOURCE_RELEASE_BEFORE_LOCKS) { /* * Release buffer pins. Note that ReleaseBuffer will remove the * buffer entry from my list, so I just have to iterate till there are * none. * * During a commit, there shouldn't be any remaining pins --- that * would indicate failure to clean up the executor correctly --- so * issue warnings. In the abort case, just clean up quietly. * * We are careful to do the releasing back-to-front, so as to avoid * O(N^2) behavior in ResourceOwnerForgetBuffer(). */ while (owner->nbuffers > 0) { if (isCommit) PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]); ReleaseBuffer(owner->buffers[owner->nbuffers - 1]); } /* * Release relcache references. Note that RelationClose will remove * the relref entry from my list, so I just have to iterate till there * are none. * * As with buffer pins, warn if any are left at commit time, and * release back-to-front for speed. */ while (owner->nrelrefs > 0) { if (isCommit) PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]); RelationClose(owner->relrefs[owner->nrelrefs - 1]); } } else if (phase == RESOURCE_RELEASE_LOCKS) { if (isTopLevel) { /* * For a top-level xact we are going to release all locks (or at * least all non-session locks), so just do a single lmgr call at * the top of the recursion. */ if (owner == TopTransactionResourceOwner) { ProcReleaseLocks(isCommit); ReleasePredicateLocks(isCommit); } } else { /* * Release locks retail. Note that if we are committing a * subtransaction, we do NOT release its locks yet, but transfer * them to the parent. */ LOCALLOCK **locks; int nlocks; Assert(owner->parent != NULL); /* * Pass the list of locks owned by this resource owner to the lock * manager, unless it has overflowed. */ if (owner->nlocks > MAX_RESOWNER_LOCKS) { locks = NULL; nlocks = 0; } else { locks = owner->locks; nlocks = owner->nlocks; } if (isCommit) LockReassignCurrentOwner(locks, nlocks); else LockReleaseCurrentOwner(locks, nlocks); } } else if (phase == RESOURCE_RELEASE_AFTER_LOCKS) { /* * Release catcache references. Note that ReleaseCatCache will remove * the catref entry from my list, so I just have to iterate till there * are none. * * As with buffer pins, warn if any are left at commit time, and * release back-to-front for speed. */ while (owner->ncatrefs > 0) { if (isCommit) PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]); ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]); } /* Ditto for catcache lists */ while (owner->ncatlistrefs > 0) { if (isCommit) PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]); ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]); } /* Ditto for plancache references */ while (owner->nplanrefs > 0) { if (isCommit) PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]); ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true); } /* Ditto for tupdesc references */ while (owner->ntupdescs > 0) { if (isCommit) PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]); DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]); } /* Ditto for snapshot references */ while (owner->nsnapshots > 0) { if (isCommit) PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]); UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]); } /* Ditto for temporary files */ while (owner->nfiles > 0) { if (isCommit) PrintFileLeakWarning(owner->files[owner->nfiles - 1]); FileClose(owner->files[owner->nfiles - 1]); } /* Clean up index scans too */ ReleaseResources_hash(); } /* Let add-on modules get a chance too */ for (item = ResourceRelease_callbacks; item; item = item->next) (*item->callback) (phase, isCommit, isTopLevel, item->arg); CurrentResourceOwner = save; }
static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, bool isTopLevel) { ResourceOwner child; ResourceOwner save; ResourceReleaseCallbackItem *item; Datum foundres; /* Recurse to handle descendants */ for (child = owner->firstchild; child != NULL; child = child->nextchild) ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel); /* * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't * get confused. */ save = CurrentResourceOwner; CurrentResourceOwner = owner; if (phase == RESOURCE_RELEASE_BEFORE_LOCKS) { /* * Release buffer pins. Note that ReleaseBuffer will remove the * buffer entry from our array, so we just have to iterate till there * are none. * * During a commit, there shouldn't be any remaining pins --- that * would indicate failure to clean up the executor correctly --- so * issue warnings. In the abort case, just clean up quietly. */ while (ResourceArrayGetAny(&(owner->bufferarr), &foundres)) { Buffer res = DatumGetBuffer(foundres); if (isCommit) PrintBufferLeakWarning(res); ReleaseBuffer(res); } /* Ditto for relcache references */ while (ResourceArrayGetAny(&(owner->relrefarr), &foundres)) { Relation res = (Relation) DatumGetPointer(foundres); if (isCommit) PrintRelCacheLeakWarning(res); RelationClose(res); } /* Ditto for dynamic shared memory segments */ while (ResourceArrayGetAny(&(owner->dsmarr), &foundres)) { dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres); if (isCommit) PrintDSMLeakWarning(res); dsm_detach(res); } /* Ditto for JIT contexts */ while (ResourceArrayGetAny(&(owner->jitarr), &foundres)) { JitContext *context = (JitContext *) PointerGetDatum(foundres); jit_release_context(context); } } else if (phase == RESOURCE_RELEASE_LOCKS) { if (isTopLevel) { /* * For a top-level xact we are going to release all locks (or at * least all non-session locks), so just do a single lmgr call at * the top of the recursion. */ if (owner == TopTransactionResourceOwner) { ProcReleaseLocks(isCommit); ReleasePredicateLocks(isCommit); } } else { /* * Release locks retail. Note that if we are committing a * subtransaction, we do NOT release its locks yet, but transfer * them to the parent. */ LOCALLOCK **locks; int nlocks; Assert(owner->parent != NULL); /* * Pass the list of locks owned by this resource owner to the lock * manager, unless it has overflowed. */ if (owner->nlocks > MAX_RESOWNER_LOCKS) { locks = NULL; nlocks = 0; } else { locks = owner->locks; nlocks = owner->nlocks; } if (isCommit) LockReassignCurrentOwner(locks, nlocks); else LockReleaseCurrentOwner(locks, nlocks); } } else if (phase == RESOURCE_RELEASE_AFTER_LOCKS) { /* * Release catcache references. Note that ReleaseCatCache will remove * the catref entry from our array, so we just have to iterate till * there are none. * * As with buffer pins, warn if any are left at commit time. */ while (ResourceArrayGetAny(&(owner->catrefarr), &foundres)) { HeapTuple res = (HeapTuple) DatumGetPointer(foundres); if (isCommit) PrintCatCacheLeakWarning(res); ReleaseCatCache(res); } /* Ditto for catcache lists */ while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres)) { CatCList *res = (CatCList *) DatumGetPointer(foundres); if (isCommit) PrintCatCacheListLeakWarning(res); ReleaseCatCacheList(res); } /* Ditto for plancache references */ while (ResourceArrayGetAny(&(owner->planrefarr), &foundres)) { CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres); if (isCommit) PrintPlanCacheLeakWarning(res); ReleaseCachedPlan(res, true); } /* Ditto for tupdesc references */ while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres)) { TupleDesc res = (TupleDesc) DatumGetPointer(foundres); if (isCommit) PrintTupleDescLeakWarning(res); DecrTupleDescRefCount(res); } /* Ditto for snapshot references */ while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres)) { Snapshot res = (Snapshot) DatumGetPointer(foundres); if (isCommit) PrintSnapshotLeakWarning(res); UnregisterSnapshot(res); } /* Ditto for temporary files */ while (ResourceArrayGetAny(&(owner->filearr), &foundres)) { File res = DatumGetFile(foundres); if (isCommit) PrintFileLeakWarning(res); FileClose(res); } } /* Let add-on modules get a chance too */ for (item = ResourceRelease_callbacks; item; item = item->next) item->callback(phase, isCommit, isTopLevel, item->arg); CurrentResourceOwner = save; }