暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss数据库源码解析系列文章——执行器解析(二)

Gauss松鼠会 2021-09-08
1125

Gauss松鼠会

学习 探索 分享数据库知识和技术 共建数据库技术交流圈

关注
上一篇openGauss数据库源码解析系列文章——执行器解析(一)介绍了“7.1 执行器整体架构及代码概览”、“7.2 执行流程”及“7.3 执行算子”的相关内容,本篇将介绍“7.4 表达式计算”及“7.5 编译执行”的精彩内容。完整版内容请查看CSDN·Gauss松鼠会专栏博客,以下内容为章节试读:

7.4  表达式计算

表达式计算对应的代码源文件是“execQual.cpp”,openGauss处理SQL语句中的函数调用、计算式和条件表达式时需要用到表达式计算。
表达式的表示方式和查询计划树的计划节点类似,通过生成表达式计划来对每个表达式节点进行计算。表达式继承层次中的公共根类为Expr节点,其他表达式节点都继承Expr节点。表达式状态的公共根类为ExprState,记录了表达式的类型以及实现该表达式节点的函数指针。表达式内存上下文类为ExprContext,ExprContext充当了计划树节点中Estate的角色,表达式计算过程中的参数以及表达式所使用的内存上下文都会存放到此结构中。
表达式计算对应的主要结构体代码如下:
    typedef struct Expr {
    NodeTag type; /*表达式节点类型*/
    } Expr;
    struct ExprState {
    NodeTag type;
    Expr* expr; /*关联的表达式节点*/
    ExprStateEvalFunc evalfunc; /*表达式运算的函数指针*/
    VectorExprFun vecExprFun;
    exprFakeCodeGenSig exprCodeGen; /*运行LLVM汇编函数的指针*/
    ScalarVector tmpVector;
    Oid resultType;
    };
    表达式计算的过程分为3个部分初始化执行清理初始化的过程使用统一接口ExecInitExpr根据表达式的类型选择不同的处理方式生成表达式节点树执行过程使用统一接口宏ExecEvalExpr执行过程类似于计划节点的递归方式

    7.4.1  初始化阶段

    ExecInitExpr函数的作用是在执行的初始化阶段,准备要执行的表达式树。根据传入的表达式node tree,来创建并返回ExprState tree。在真正的执行阶段会根据ExprState tree中记录的处理函数,递归地执行每个节点。ExecInitExpr函数的核心代码如下:
      if (node == NULL) {  /* 判断输入是否为空 */
      gstrace_exit(GS_TRC_ID_ExecInitExpr);
      return NULL;}
      switch (nodeTag(node)) { /* 根据节点类型初始化节点内容 */
      case T_Var:
      case T_Const:
      case T_Param:
      ……
      case T_CaseTestExpr:
      case T_Aggref:
      ……
      case T_CurrentOfExpr:
      case T_TargetEntry:
      case T_List:
      case T_Rownum:
      default:…… }
      return state; /* 返回表达式节点树 */
      ExecInitExpr函数主要执行流程如下。
      (1) 判断输入的node节点是否为空,若为空,则直接返回NULL,表示没有表达式限制。
      (2) 根据输入的node节点的类型初始化变量evalfunc即node节点对应的执行函数,若节点存在参数或者表达式,则递归调用ExecInitExpr函数,最后生成ExprState tree。
      (3) 返回ExprState tree,在执行表达式的时候会根据ExprState tree来递归执行。
      ExecInitExpr函数流程如图7-12所示。

      图7-12  ExecInitExpr函数执行流程

      7.4.2  执行阶段

      执行阶段主要是根据宏定义ExecEvalExpr递归调用执行函数。在计算时的核心函数包括ExecMakeFunctionResult和ExecMakeFunctionResultNoSets,通过这两个函数计算出表达式的结果并返回。其他的表达式计算函数还包括ExecEvalFunc、ExecEvalOper、ExecEvalScalarVar、ExecEvalConst、ExecQual、ExecProject等,这些函数分别对应不同的表达式的类型或者参数类型,通过不同的逻辑来处理获取的计算结果。
      执行过程就是上层函数调用下层函数。首先下层函数根据参数类型获取相应的数据,然后上层函数通过处理数据得到最后的结果,最后根据表达式逻辑返回结果。
      通过一个简单的SQL语句介绍一下表达式计算的函数调用过程,每种SQL语句的执行流程不完全一致,此示例仅供参考。例句:“SELECT * FROM s WHERE s.a<3 or s.b<3;”。具体流程如下。
      (1) 根据表达式“s.a<3 or s.b<3”确认第一步调用ExecQual函数。
      (2) 由于本次表达式是or语句,所以需要将表达式传入到ExecEvalOr函数计算,在ExecEvalOr函数中采用for循环依次对子表达式“s.a<3”和“s.b<3”计算,将子表达式传入到下一层函数中。
      (3) ExecEvalOper函数根据子表达式的返回值是否为set集来调用下一层函数,计算子表达式的结果。
      (4) ExecMakeFunctionResultNoSets函数中获取子表达式中的参数的值,“s.a”和“3”分别通过ExecEvalScalarVar函数和ExecEvalConst函数来获取,获取到参数之后计算表达式结果,若s.a<3本次计算返回true,否则返回false,并依次向上层返回结果。
      函数调用流程图如图7-13所示。

      图7-13  函数调用执行流程

      执行阶段所有函数都共享此调用约定,相关代码如下:

        输入:
        expression:需要计算的表达式状态树。
        econtext:评估上下文信息。
        输出:
        return value:Datum类型的返回值。
        *isNull:如果结果为NULL,则设置为TRUE(实际返回值无意义);如果结果非空,则设置为FALSE
        *isDone:设置为set-result状态的指标。
        ……(本节内容未完)
        为提升您的阅读体验,完整版内容已运用专业格式发布到CSDN·Gauss松鼠会专栏,请扫码下方二维码,“关注”后进行内容阅读或点击文末“阅读原文”进入博客进行学习~







        Gauss松鼠会
        汇集数据库从业人员及爱好者
        互助解决问题 共建数据库技术交流圈


        完整版内容,请点击“阅读原文”

        文章转载自Gauss松鼠会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论