引言
上一篇文章中,我们学习了内存上下文的只是,并且对PortalStart函数做了简要的了解。今天这篇文章,我将学习PortalRun函数的有关内容。
执行过程(续前文)
PortalRun
//代码清单1
bool PortalRun(
Portal portal, long count, bool isTopLevel, DestReceiver* dest, DestReceiver* altdest, char* completionTag)
{
············
/*
* Check for improper portal use, and mark portal active.
*/
MarkPortalActive(portal);
QueryDesc* queryDesc = portal->queryDesc;
PotalSetIoState(portal);
············
PG_TRY();
{
switch (portal->strategy) {
case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
case PORTAL_ONE_MOD_WITH:
case PORTAL_UTIL_SELECT:
/*
* If we have not yet run the command, do so, storing its
* results in the portal's tuplestore. But we don't do that
* for the PORTAL_ONE_SELECT case.
*/
if (portal->strategy != PORTAL_ONE_SELECT && !portal->holdStore) {
/* DestRemoteExecute can not send T message automatically */
if (strcmp(portal->commandTag, "EXPLAIN") == 0 && dest->mydest != DestRemote)
t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL;
FillPortalStore(portal, isTopLevel);
}
/*
* Now fetch desired portion of results.
*/
nprocessed = PortalRunSelect(portal, true, count, dest);
/*
* If the portal result contains a command tag and the caller
* gave us a pointer to store it, copy it. Patch the "SELECT"
* tag to also provide the rowcount.
*/
if (completionTag != NULL) {
if (strcmp(portal->commandTag, "SELECT") == 0) {
errorno = snprintf_s(completionTag,
COMPLETION_TAG_BUFSIZE,
COMPLETION_TAG_BUFSIZE - 1,
"SELECT %lu",
nprocessed);
securec_check_ss(errorno, "\0", "\0");
} else {
errorno = strcpy_s(completionTag, COMPLETION_TAG_BUFSIZE,
portal->commandTag);
securec_check(errorno, "\0", "\0");
}
}
/* Mark portal not active */
portal->status = PORTAL_READY;
/*
* Since it's a forward fetch, say DONE iff atEnd is now true.
*/
result = portal->atEnd;
break;
case PORTAL_MULTI_QUERY:
PortalRunMulti(portal, isTopLevel, dest, altdest, completionTag);
/* Prevent portal's commands from being re-executed */
MarkPortalDone(portal);
/* Always complete at end of RunMulti */
result = true;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmodule(MOD_EXECUTOR),
errmsg("Unrecognized portal strategy: %d", (int)portal->strategy)));
result = false; /* keep compiler quiet */
break;
}
}
PG_CATCH();
{
}
PG_END_TRY();
u_sess->plsql_cxt.portal_depth = savePortalDepth;
stp_reset_xact_state_and_err_msg(savedisAllowCommitRollback, needResetErrMsg);
············
/* update unique sql stat */
if (is_instr_top_portal() && is_unique_sql_enabled() && is_local_unique_sql()) {
/* Instrumentation: update unique sql returned rows(SELECT) */
// only CN can update this counter
if (portal->queryDesc != NULL && portal->queryDesc->estate && portal->queryDesc->estate->es_plannedstmt &&
portal->queryDesc->estate->es_plannedstmt->commandType == CMD_SELECT) {
ereport(DEBUG1,
(errmodule(MOD_INSTR),
errmsg("[UniqueSQL]"
"unique id: %lu , select returned rows: %lu",
u_sess->unique_sql_cxt.unique_sql_id,
portal->queryDesc->estate->es_processed)));
UniqueSQLStatCountReturnedRows(portal->queryDesc->estate->es_processed);
}
/* PortalRun using unique_sql_start_time as unique sql elapse start time */
if (IsNeedUpdateUniqueSQLStat(portal) && IS_UNIQUE_SQL_TRACK_TOP && IsTopUniqueSQL()) {
UpdateUniqueSQLStat(NULL, NULL, u_sess->unique_sql_cxt.unique_sql_start_time);
}
if (u_sess->unique_sql_cxt.unique_sql_start_time != 0) {
int64 duration = GetCurrentTimestamp() - u_sess->unique_sql_cxt.unique_sql_start_time;
if (IS_SINGLE_NODE) {
pgstat_update_responstime_singlenode(
u_sess->unique_sql_cxt.unique_sql_id, u_sess->unique_sql_cxt.unique_sql_start_time, duration);
} else {
pgstat_report_sql_rt(
u_sess->unique_sql_cxt.unique_sql_id, u_sess->unique_sql_cxt.unique_sql_start_time, duration);
}
}
}
decrease_instr_portal_nesting_level();
gstrace_exit(GS_TRC_ID_PortalRun);
u_sess->pcache_cxt.cur_stmt_name = old_stmt_name;
return result;
}
此函数的主要功能为:
-
调用MarkPortalActive函数,使portal从Ready状态转为Active状态
-
执行Switch-Case语句,根据不同语句,选择不同的执行方法。
-
PORTAL_ONE_SELECT 、 PORTAL_ONE_RETURNING 、 PORTAL_ONE_MOD_WITH 和 PORTAL_UTIL_SELECT:
如果portal对应的语句还没有被执行过(portal->holdStore字段为空),并且不是PORTAL_ONE_SELECT语句,则调用FillPortalStore函数执行查询并将结果加载到portal的存储元组中。
然后调用PortalRunSelect函数获取结果。
把portal更新为Ready状态,准备下一次执行。
-
PORTAL_MULTI_QUERY:
调用PortalRunMulti函数执行此语句(一般为多个查询或者非查询语句,例如对表的创建和删除等)。并把portal更新为Done状态。
-
-
执行后后续的一些恢复及其他操作,例如恢复原来的内存上下文,记录和更新一些时间和资源的统计信息等。
在学习PortalRun函数的过程中,我发现了两个陌生的点,并对其深入拓展学习了一番。
1.Portal对象的持久化连接和非持久化连接
在代码清单1中,第28行中出现了holdStore(持久化存储)字段,通过引申学习,我发现Portal对象有两种连接方式。
-
非持久化连接
如果查询是一个简单的SELECT语句,那么查询结果不会存储在任何地方,而是直接返回给客户端。此时Portal对象是非持久化连接,即当事务结束时就释放内存。
-
持久化连接
如果查询是一个带有游标(CURSOR)的SELECT语句,那么查询中间结果会储存在Portal对象中。此时Portal对象是持久化连接,可以让Portal对象在多个请求——响应事务中保持有效,而不是每次都重新创建和销毁。
2.UNIQUE SQL
在代码清单1中的第98行~128行代码中,我发现unique_sql字符串频繁出现,我查找了相关资料,发现opengauss数据库执行过程的一个精妙之处。
在opengauss代码架构中,拓扑结构相同的SQL语句会赋给一个相同ID。
例如,有如下两条SQL语句:
select* from t where id=1
select* from t where id=2
这两条SQL语句生成的查询树的拓扑结构是相同的,只有选择算子的输入不同,因此属于同一个UNIQUE SQL。
引入UNIQUE SQL后SQL语句的执行流程如下图所示。
可以看出,当UNIQUE ID存在时,SQL可以省去创建UNIQUE SQL,也即创建查询树的过程。这样对代码的执行性能又有了一个提升。
小结
本篇文章对PortalRun函数做了简要的分析,学习了Portal对象的持久性和非持久性连接,并对UNIQUE SQL有了一定了解。下一篇文章中,我将学习Portal相关的最后一个函数PortalDrop。