源码链接
概述
在前两篇博客对pquery.cpp的解析(一)以及对pquery.cpp的解析(二)中,我们分析了 Portal 模块中的 PortalStart() 和 PortalRun() 函数。不过,当我们分别用它们启动和运行了 portal 时,我们在使用完后还要回收分配给它的内存空间,这时候就用到了 PortalDrop() 函数,这篇文章我来解析一下 portalmem.cpp 中这个十分重要的函数。
解析
PortalDrop()
//代码清单1
//src/common/backend/utils/mmgr/portalmem.cpp
void PortalDrop(Portal portal, bool isTopCommit)
{
······
if (PointerIsValid(portal->cleanup)) {
(*portal->cleanup)(portal);
portal->cleanup = NULL;
}
PortalHashTableDelete(portal);
PortalReleaseCachedPlan(portal);
if (portal->resowner && (!isTopCommit || portal->status == PORTAL_FAILED)) {
bool isCommit = (portal->status != PORTAL_FAILED);
ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_BEFORE_LOCKS, isCommit, false);
ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_LOCKS, isCommit, false);
ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_AFTER_LOCKS, isCommit, false);
ResourceOwnerDelete(portal->resowner);
}
portal->resowner = NULL;
#ifndef ENABLE_MULTIPLE_NODES
if (portal->holdStore && !portal->isAutoOutParam) {
#else
if (portal->holdStore) {
#endif
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(portal->holdContext);
tuplestore_end(portal->holdStore);
MemoryContextSwitchTo(oldcontext);
portal->holdStore = NULL;
}
······
pfree(portal);
}
该函数的作用是释放之前为 portal 分配的内存空间,先是处理与 portal 相关联的各个成员变量,将它们处理后再释放内存。不难看出,该函数调用了多个内存清理函数。代码清单1第6~9行的 if 判断语句块一般来说是在 MarkPortalDone() 或 MarkPortalFailed() 中已经被执行的, MarkPortalDone() 或 MarkPortalFailed() 的代码大体如下:
//代码清单2
//src/common/backend/utils/mmgr/portalmem.cpp
void MarkPortalDone(Portal portal)
{
Assert(portal->status == PORTAL_ACTIVE);
portal->status = PORTAL_DONE;
······
if (PointerIsValid(portal->cleanup)) {
(*portal->cleanup)(portal);
portal->cleanup = NULL;
}
}
//代码清单3
//src/common/backend/utils/mmgr/portalmem.cpp
void MarkPortalFailed(Portal portal)
{
Assert(portal->status != PORTAL_DONE);
portal->status = PORTAL_FAILED;
······
if (PointerIsValid(portal->cleanup)) {
(*portal->cleanup)(portal);
portal->cleanup = NULL;
}
}
换句话说,当 portal 功能行使完毕或发生故障时,我们会将它的状态修改完成或是故障,然后调用 portal 的成员变量 cleanup 指向的钩子函数来处理 portal ,这些都是在调用 PortalDrop() 摧毁 portal 前完成的。
代码清单1第10行利用宏函数 PortalHashTableDelete() 将 portal 从哈希表中移除,第11行利用函数 PortalReleaseCachedPlan() 用来取消 portal 查询缓存中的计划,当然前提是存在。提一句,这两个函数都在此文件中。
代码清单1第13~19行的 if 判断语句块用来释放仍然与 portal 连接着的各种资源,第27~32行的语句块在 portal 中有元组存在的情况下必须执行,第35行用来释放为 portal 变量释放的内存。
总结
一句话,PortalDrop() 在 Portal 模块中就是用来善后的,用来释放与存储该模块执行状态的变量 portal 连接的资源以及 portal 自身。