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

He3DB逻辑优化之常量表达式化简

原创 努力学习中 2023-11-02
220

一条SQL在He3DB中会经过逻辑优化阶段,这里我们分析一下其中的一个逻辑优化过程,常量表达式化简。对此进行分析。

常量表达式化简

常量表达式可以进行化简,可降低执行器计算表达式的代价。在逻辑优化阶段,会判断是否可以进行常量表达式化简,如果可以,则在执行器执行之前就预先对常量表达式树进行计算,计算出常量后,以新计算出的常量表达式代替原有的表达式。当进入执行器时,此时表达式已被替换为常量,避免了在执行器中频繁的计算表达式。

化简示例

我们以下面的SQL为例,条件表达式为a > 1 + 1,是常量表达式,可以进行化简。

-- 常量表达式可以进行化简 postgres=# explain select * from t1 where a > 1 + 1; QUERY PLAN ----------------------------------------------------- Seq Scan on t1 (cost=0.00..17.50 rows=998 width=8) Filter: (a > 2) -- 进入执行器中, 表达式已被提前替换为常量,每扫描一个元组,不用再进行1+1的表达式计算了 (2 rows) -- 不满足常量表达式化简的条件 postgres=# explain select * from t1 where a > 1 + random(); QUERY PLAN ------------------------------------------------------------------------ Seq Scan on t1 (cost=0.00..25.00 rows=333 width=8) Filter: ((a)::double precision > ('1'::double precision + random())) -- 执行器中每扫描一个元组,都要进行一次表达式计算 (2 rows)

除了常量表达式,常量函数也可以在逻辑优化阶段提前执行,计算得到常量,用常量替换原有的函数表达式。

源码分析

我们以select * from t1 where a = 1 + 1;这条语句为例,分析一下在He3DB中是如何进行化简的。主流程如下

exec_simple_query --> pg_parse_query --> raw_parser --> base_yyparse --> pg_analyze_and_rewrite --> transformStmt --> transformSelectStmt --> transformWhereClause --> transformExpr --> pg_plan_queries --> pg_plan_query --> planner --> standard_planner --> subquery_planner --> preprocess_qual_conditions --> preprocess_expression --> eval_const_expressions // 在这里完成常量表达式化简,完成1+1表达式替换为常量2 --> eval_const_expressions_mutator --> simplify_function --> evaluate_function --> evaluate_expr // 1+1的表达式被计算为常量2 --> create_plan --> PortalStart --> PortalRun // 执行器执行 --> PortalDrop

可以看到,在逻辑优化阶段,常量表达式已被化简为常量,进入执行器时,表达式树已被替换为常量。

/* evaluate_expr: pre-evaluate a constant expression*/ Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation) { EState *estate; ExprState *exprstate; MemoryContext oldcontext; Datum const_val; bool const_is_null; int16 resultTypLen; bool resultTypByVal; /* * To use the executor, we need an EState. */ estate = CreateExecutorState(); /* We can use the estate's working context to avoid memory leaks. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* Make sure any opfuncids are filled in. */ fix_opfuncids((Node *) expr); /* * Prepare expr for execution. (Note: we can't use ExecPrepareExpr * because it'd result in recursively invoking eval_const_expressions.) */ exprstate = ExecInitExpr(expr, NULL); /* * And evaluate it. * * It is OK to use a default econtext because none of the ExecEvalExpr() * code used in this situation will use econtext. That might seem * fortuitous, but it's not so unreasonable --- a constant expression does * not depend on context, by definition, n'est ce pas? */ const_val = ExecEvalExprSwitchContext(exprstate, GetPerTupleExprContext(estate), &const_is_null); // 计算表达式 1 + 1 /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); /* * Must copy result out of sub-context used by expression eval. * * Also, if it's varlena, forcibly detoast it. This protects us against * storing TOAST pointers into plans that might outlive the referenced * data. (makeConst would handle detoasting anyway, but it's worth a few * extra lines here so that we can do the copy and detoast in one step.) */ if (!const_is_null) { if (resultTypLen == -1) const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val)); else const_val = datumCopy(const_val, resultTypByVal, resultTypLen); } /* Release all the junk we just created */ FreeExecutorState(estate); /* * Make the constant result node. */ return (Expr *) makeConst(result_type, result_typmod, result_collation, resultTypLen, const_val, const_is_null, resultTypByVal); }
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论