源码链接
概述
该文件中有 ProcessUtility 模块中最重要的函数,即 ProcessUtility() 函数,而实际执行该模块的功能的还是 standard_ProcessUtility() 函数,本篇博客简单地解析一下这两个函数。
解析
ProcessUtility()
//代码清单1
//src/gausskernel/process/tcop/utility.cpp
void ProcessUtility(Node* parse_tree, const char* query_string, ParamListInfo params, bool is_top_level, DestReceiver* dest,
#ifdef PGXC
bool sent_to_remote,
#endif /* PGXC */
char* completion_tag,
bool isCTAS)
{
/* required as of 8.4 */
AssertEreport(query_string != NULL, MOD_EXECUTOR, "query string is NULL");
/*
* We provide a function hook variable that lets loadable plugins get
* control when ProcessUtility is called. Such a plugin would normally
* call standard_ProcessUtility().
* it's unsafe to deal with plugins hooks as dynamic lib may be released
*/
if (ProcessUtility_hook && !(g_instance.status > NoShutdown))
(*ProcessUtility_hook)(parse_tree,
query_string,
params,
is_top_level,
dest,
#ifdef PGXC
sent_to_remote,
#endif /* PGXC */
completion_tag,
isCTAS);
else
standard_ProcessUtility(parse_tree,
query_string,
params,
is_top_level,
dest,
#ifdef PGXC
sent_to_remote,
#endif /* PGXC */
completion_tag,
isCTAS);
}
openGauss 在内部是采用 PGXC 架构的,所以在条件编译命令 #ifdef 和 #endif 之间的语句均执行。在之前的博客:对execMain.cpp的解析(一)中,我们已经知道钩子类型是空类型,那么 ProcessUtility_hook 自然是 NULL ,所以 ProcessUtility() 将调用 else 语句中的 standard_ProcessUtility() ,接下来解析一下 standard_ProcessUtility()。
standard_ProcessUtility()
//代码清单2
//src/gausskernel/process/tcop/utility.cpp
void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamListInfo params, bool is_top_level,
DestReceiver* dest,
#ifdef PGXC
bool sent_to_remote,
#endif /* PGXC */
char* completion_tag,
bool isCTAS)
{
······
switch (nodeTag(parse_tree)) {
case T_TransactionStmt: {
TransactionStmt* stmt = (TransactionStmt*)parse_tree;
switch (stmt->kind) {
case TRANS_STMT_BEGIN:
case TRANS_STMT_START: {
ListCell* lc = NULL;
BeginTransactionBlock();
foreach (lc, stmt->options) {
DefElem* item = (DefElem*)lfirst(lc);
set_item_arg_according_to_def_name(item);
}
u_sess->need_report_top_xid = true;
} break;
case TRANS_STMT_COMMIT:
/* Only generate one time when u_sess->debug_query_id = 0 in CN */
if ((IS_SINGLE_NODE || IS_PGXC_COORDINATOR) && u_sess->debug_query_id == 0) {
u_sess->debug_query_id = generate_unique_id64(>_queryId);
pgstat_report_queryid(u_sess->debug_query_id);
}
/* only check write nodes csn valid */
if (TransactionIdIsValid(GetTopTransactionIdIfAny())) {
CheckProcCsnValid();
}
if (!EndTransactionBlock()) {
/* report unsuccessful commit in completion_tag */
if (completion_tag != NULL) {
errorno = strcpy_s(completion_tag, COMPLETION_TAG_BUFSIZE, "ROLLBACK");
securec_check(errorno, "\0", "\0");
}
}
FreeSavepointList();
break;
······
case T_DropSubscriptionStmt:
······
DropSubscription((DropSubscriptionStmt *) parse_tree, is_top_level);
break;
default: {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized node type: %d", (int)nodeTag(parse_tree))));
} break;
}
}
该函数最重要的还是它的 switch case 结构,不同的功能性语句需要执行不同的分支。另外,在每个分支下,可能还有更加特定的语句。就比如这个函数中的第一个分支 T_TransactionStmt ,在这个分支下,又有 TRANS_STMT_BEGIN、TRANS_STMT_START 和 TRANS_STMT_COMMIT 的小分支。总地来说,这个函数的功能就是处理特定的功能性语句。
总结
虽然我不能像解析其它模块一样详细地解析一下 ProcessUtility 模块,但是,我们却能很清晰地了解它的功能以及它的结构。这个模块的入口函数是 ProcessUtility() 函数,而它又调用了standard_ProcessUtility() 函数,最后通过判定特定的语句分支选择执行语句块,这就完成了一条功能性语句的执行。