openGauss学习——optimizer主调函数

引言

在上篇博客中我对查询优化optimizer进行了简单概述。在本篇博客中,我将对查询优化相关的主调函数的调用情况进行简要解读。

exec_simple_query()

这个函数位于通信管理模块tcop(/src/gausskernel/process/tcop)下的文件postgres.cpp中,它由Postgres服务线程调用,用来完成对用户输入的“简单查询语句”的执行工作。

/*
 * exec_simple_query
 *
 * Execute a "simple Query" protocol message.
 */
static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)

可以看到,在代码line 2504~2507的位置,函数调用pg_analyze_and_rewrite() 完成对用户输入的查询语句的解析和重写工作。

     if (HYBRID_MESSAGE != messageType)
        querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0);
    else
        querytree_list = pg_analyze_and_rewrite(parsetree, sql_query_string, NULL, 0);

其中传入的参数parsetree是已经初始化的解析树结点,它的类型是Node*。

Node* parsetree = (Node*)lfirst(parsetree_item);

函数pg_analyze_and_rewrite调用完成后,我们就得到了已经解析并应用重写规则重写后的查询树列表,保存在querytree_list中,它是一个List* 类型的变量。

List* querytree_list = NULL;

在查询解析和重写工作完成之后,其最终产物——查询树链表将被移交给查询规划模块,该模块的入口函数是pg_plan_queries,其调用位置在函数代码的line 2559位置。这个函数负责将查询树链表变成执行计划链表。pg_plan_queries 只会处理非Utility命令,它调用pg_plan_query对每一个查询树进行处理,并将生成的 PlannedStmt结构体(上篇博客中有简单介绍)构成一个链表(执行计划链表)返回。

plantree_list = pg_plan_queries(querytree_list, 0, NULL);

执行计划链表保存在plantree_List中,它也是一个List* 类型的变量。

List* plantree_list = NULL;

pg_plan_queries()

重要部分源码及注释如下:

    foreach (query_list, querytrees) {
        Query* query = castNode(Query, lfirst(query_list));
        Node* stmt = NULL;
        PlannedStmt* ps = NULL;
        if (query->commandType == CMD_UTILITY) {
            /* Utility commands have no plans. */
            stmt = query->utilityStmt;
            /* output grammer error of hint */
            output_utility_hint_warning((Node*)query, DEBUG1);
        } else {
            query->boundParamsQ = boundParams;
            bool is_insert_multiple_values = is_insert_multiple_values_query_in_gtmfree(query);
            /* Temporarily apply SET hint using PG_TRY for later recovery */
            int nest_level = apply_set_hint(query);
            PG_TRY();
            {
                stmt = (Node*)pg_plan_query(query, cursorOptions, boundParams);
            }
            PG_CATCH();
            {
                recover_set_hint(nest_level);
                PG_RE_THROW();
            }
            PG_END_TRY();
            ……

如前面所说,pg_plan_queries 只会处理非Utility命令(包括notify,alter,copy等),如果传入的查询树链表中包括Utility命令,它会向外抛出一个警告,并且不为这条查询语句生成执行计划。如果是non-Utility命令(包括SELECT/INSERT/UPDATE/DELETE/MERGE),则调用pg_plan_query对查询树进行处理,并将生成的 PlannedStmt结构体(构成一个链表(执行计划链表)返回。

pg_plan_query()

函数头部注释:

/*
 * Generate a plan for a single already-rewritten query.
 * This is a thin wrapper around planner() and takes the same parameters.
 */

本函数为一个已经完成重写的查询生成执行计划。pg_plan_query中负责实际计划生成的是planner函数,并且通常认为从planner函数开始就进入了执行计划的生成阶段。函数是对planner函数的一个包装函数,并且它们的参数几乎相同。对于optimizer的入口函数planner,上篇博客中有简单介绍。

    /* call the optimizer */
    plan = planner(querytree, cursorOptions, boundParams);

对planner函数的调用结果保存在变量plan中,它是PlannedStmt* 类型的。在调用完成后,经过对生成的执行计划的检查和必要的输出工作(Print plan if debugging)之后,将执行计划返回给pg_plan_queries函数。

return plan;

standard_planner()

在上篇博客中我们介绍过,函数planner在大多数情况下会调用standard_planner来进行实际的查询优化。standard_planner的返回值是一个PlannerStmt结构,其中包含执行器执行计划时的全部信息,包括计划树(Plan)、子计划树和相关参数信息。

PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo boundParams)

函数standard_planner位于planner.cpp中,其有三个入口参数:

  1. parse:一个Query结构,表示需要处理的查询树。

  2. cursorOption:整型,表示游标选项,在处理与游标操作时用到。

  3. ParamListInfo 结构,记录了所用到的参数信息。

至于实际将查询树转化为计划树的工作standard_planner通过调用subquery_planner函数来实现。subquery_planner接收Query查询树,返回Plan (计划树),该计划树被包装在PlannedStmt中。对含有子查询的情况,可通过递归调用为子查询生成相应的子计划并链接到上层计划。该函数主要调用相关预处理函数,依据消除冗余条件、减少递归层数、简化路径生成等原则对Query查询树进行预处理。

    /* primary planning entry point (may recurse for subqueries) */
    top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root);

对于函数subquery_planner在下一篇博客中会进行介绍。

函数功能描述
exec_simple_query执行用户输入的简单查询语句。
pg_plan_queries将查询树链表变成执行计划链表。调用pg_plan_query以完成单个查询的转化。
pg_plan_query将单个查询树转化成计划树。是planner的包装函数。
planneroptimizer入口函数。输入查询树,输出计划树。
standard_planner标准的规划处理。输入查询树,输出计划树。
subquery_planner优化处理的主体函数。可以递归处理子查询。

总结

在本篇博客中我对optimizer的主调函数进行了简单介绍。下一篇博客中我会对函数 subquery_planner() 进行学习和解读工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值