当前位置: 首页 > news >正文

Postgresql源码(137)执行器参数传递与使用

参考
《Postgresql源码(127)投影ExecProject的表达式执行分析》

0 总结速查

prepare p_04(int,int) as select b from tbl_01 where a = $1 and b = $2为例。

  • custom计划中,在表达式计算中使用参数的值,因为custom计划会带参数值,所以表达式计算时不需要从参数列表中取值,直接EEO_CASE(EEOP_FUNCEXPR_STRICT)计算即可。
  • generic计划中,在表达式计算中使用参数的值,generic计划不带参数值,所以需要多走一步EEO_CASE(EEOP_PARAM_EXTERN)、EEO_CASE(EEOP_FUNCEXPR_STRICT)。具体调用ExecEvalParamExtern函数拿到参数值。
  • generic计划中,使用参数的节点会放一个Param。

1 执行器如何使用参数?

在下面例子中,execute语句执行时带了两个参数,执行器是在哪里带入参数执行的?下面做一些分析。

prepare p_04(int,int) as select b from tbl_01 where a = $1 and b = $2;
execute p_04(2,200);

带入参数的位置是PortalStart函数。

extern void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot);

PortalStart的第二个参数可以接受参数列表ParamListInfo,从代码上看,PortalStart有三处调用位置:

exec_simple_queryPortalStart(portal, NULL, 0, InvalidSnapshot)exec_bind_messagePortalStart(portal, params, 0, InvalidSnapshot)ExecuteQueryPortalStart(portal, paramLI, eflags, GetActiveSnapshot())SPI_cursor_open_internalPortalStart(portal, paramLI, 0, snapshot)
  1. exec_simple_query:执行简单SQL语句,不需要传参。
  2. exec_bind_message:PEB协议中的B阶段,绑定参数。
  3. ExecuteQuery:SQL语法提供的PBE协议,在execute阶段,绑定参数。
  4. SPI_cursor_open_internal:PL中打开游标时,绑定参数。

下面对3、4两种情况展开分析。

2 execute执行时执行器的传参

栗子:

create table tbl_01(a int, b int);
insert into tbl_01 values (1,100);
insert into tbl_01 values (2,200);
prepare p_04(int,int) as select b from tbl_01 where a = $1 and b = $2;
execute p_04(2,200);

execute p_04(2,200); 执行时,PortalStart会带入ParamListInfo。
在这里插入图片描述

2.1 问题一:ParamListInfo的来源?

ExecuteQueryEvaluateParams// 准备计算参数表达式的值exprstates = ExecPrepareExprList(params, estate)makeParamListretval->parserSetup = paramlist_parser_setup// paramlist_parser_setup//   pstate->p_paramref_hook = paramlist_param_ref//// paramlist_param_ref//   param = makeNode(Param)//   param->paramkind = PARAM_EXTERN//   param->paramid = paramno//   ...//   钩子函数中生成Param,该实例不会走这个钩子,注意Param结构是不带值的,只用于中间过程。// 走表达式计算参数的值// 注意值都在paramLI->params[i]->value中i = 0;foreach(l, exprstates){ExprState  *n = (ExprState *) lfirst(l);ParamExternData *prm = &paramLI->params[i];prm->ptype = param_types[i];prm->pflags = PARAM_FLAG_CONST;prm->value = ExecEvalExprSwitchContext(n,GetPerTupleExprContext(estate),&prm->isnull);i++;}

生成ParamListInfo(包含参数的全部信息,包括值)

paramLI = {paramFetch = 0x0,paramFetchArg = 0x0,paramCompile = 0x0,paramCompileArg = 0x0,parserSetup = 0x8357c1 <paramlist_parser_setup>,parserSetupArg = 0x3238da8,paramValuesStr = 0x0,numParams = 2,params = 0x3238de8}

2.2 问题二:ParamListInfo的使用位置?

custom plan

前五次执行会生成customplan,customplan在GetCachedPlan中生成,customplan计划是带着参数生成的(按参数定制的计划,更准确,但每次参数变了都要重新生成),所以计划中会有参数的具体值,执行时不会使用执行器带入的ParamListInfo。
在这里插入图片描述
构造customplan时,会带入参数生成计划:
在这里插入图片描述

ExecQual调用表达式框架计算where a = $1 and b = $2,ExecQual函数进入表达计算。

ExecScanfor (;;)slot = ExecScanFetch...if (qual == NULL || ExecQual(qual, econtext))...return slot

ExecQual表达式计算流程:

  1. EEO_CASE(EEOP_SCAN_FETCHSOME)
    • 从econtext->ecxt_scantuple读取到scanslot(当前要处理的一行数据)slot_getsomeattrs函数确保这一行数据中,至少有op->d.fetch.last_var个列是可以直接访问的。《Postgresql源码(127)投影ExecProject的表达式执行分析》
  2. EEO_CASE(EEOP_SCAN_VAR)
    • 从行中拿到需要列的值。《Postgresql源码(127)投影ExecProject的表达式执行分析》
  3. EEO_CASE(EEOP_FUNCEXPR_STRICT)
    • 计算where中第一个条件
    • fcinfo : int4eq
    • arg1 : 2
    • arg2 : 2
  4. EEO_CASE(EEOP_QUAL)
  5. EEO_CASE(EEOP_FUNCEXPR_STRICT)
    • 计算where中第二个条件
    • fcinfo : int4eq
    • arg1 : 200
    • arg2 : 200
  6. EEO_CASE(EEOP_QUAL)

generic plan

第六次执行时,choose_custom_plan返回false,BuildCachedPlan时没有传入任何参数构造generic plan(通用执行计划,可以接受任何参数,不用每次都生成计划,但没有customplan精确,毕竟这里没有给参数)
在这里插入图片描述
执行execute p_04(2,200);给的两个参数明显是用于过滤条件的,所以在ExecScan→ExecQual中寻找使用的位置:
在这里插入图片描述

在ExecEvalParamExtern函数中,从econtext->ecxt_param_list_info中拿到参数值,返回给表达式框架:

ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
{ParamListInfo paramInfo = econtext->ecxt_param_list_info;int			paramId = op->d.param.paramid;if (likely(paramInfo &&paramId > 0 && paramId <= paramInfo->numParams)){ParamExternData *prm;ParamExternData prmdata;......prm = &paramInfo->params[paramId - 1];......*op->resvalue = prm->value;*op->resnull = prm->isnull;return;}}......
}

最终ExecScan在loop中,拿到的slot经过ExecQual的计算,决定是否保留该元组。

ExecQual调用表达式框架计算where a = $1 and b = $2,通过ExecEvalParamExtern函数拿到$1$2的值,完成计算,放回true OR false。

ExecScanfor (;;)slot = ExecScanFetch...if (qual == NULL || ExecQual(qual, econtext))...return slot

表达式计算流程:

  1. EEO_CASE(EEOP_SCAN_FETCHSOME)
  2. EEO_CASE(EEOP_SCAN_VAR)
  3. EEO_CASE(EEOP_PARAM_EXTERN)
  4. EEO_CASE(EEOP_FUNCEXPR_STRICT)
    • int4eq
    • arg1 : 2
    • arg2 : 2
  5. EEO_CASE(EEOP_QUAL)
  6. EEO_CASE(EEOP_SCAN_VAR)
  7. EEO_CASE(EEOP_PARAM_EXTERN)
  8. EEO_CASE(EEOP_FUNCEXPR_STRICT)
    • int4eq
    • arg1 : 200
    • arg2 : 200

从计划上来看,参数的位置是两个Param。
在这里插入图片描述

3 游标打开时执行器的传参

create table tbl_01(a int, b int);
insert into tbl_01 values (1,100);
insert into tbl_01 values (2,200);do $$
declareres int;c_04 CURSOR (p1 int, p2 int) FOR select b from tbl_01 where a = p1 and b = p2;
beginopen c_04(2,200);fetch c_04 into res;raise notice '%',res;
end; $$;

PL的参数在transform阶段已经转成const了,所以执行类似customplan:
在这里插入图片描述

fetch堆栈:
在这里插入图片描述

http://www.lryc.cn/news/475010.html

相关文章:

  • 韩国恋爱游戏:阿西, 美女室友竟然…?百度网盘下载
  • 一个运维牛人对运维规则的10个总结
  • Istio基本概念及部署
  • 基于 Python 的 Django 框架开发的电影推荐系统
  • 离线数仓开发SQL编写和调试的最佳实践(如何又快又好完成任务,学会几条就不用当很辛苦的牛马)
  • PostgreSQL 增量备份:保护你的数据资产
  • 字节青训-寻找最大葫芦
  • el-checkbox勾选一个变成了勾选所有
  • ExpandingCard扩展卡片
  • 移远通信推出八款天线新品,覆盖5G、4G、Wi-Fi和LoRa领域
  • MySQL 9从入门到性能优化-创建触发器
  • UE5 第三人称学习之动画 control rig
  • C++之--初见模板初阶
  • Nature|用于无线监测颅内信号的植入式柔性超声波传感器(柔性传感/健康监测/植入式电子/水凝胶)
  • 【和AI的《趣味》聊天】01 AI:你找茬是吧(
  • “发放父作业单”是“过数”用例里面的内容吗
  • Linux补基础之:网络配置
  • 【flink】之kafka到kafka
  • 微信小程序时间弹窗——年月日时分
  • 杂货 | 每日资讯 | 2024.11.1
  • Genmoai-smol:专为单 GPU 优化的开源 AI 视频生成模型,低显存生成高质量视频
  • RHCE8
  • 长短期记忆网络(LSTM)如何在连续的时间步骤中处理信息
  • MySQL基础(三)
  • 浏览器八股
  • 华为机试HJ18 识别有效的IP地址和掩码并进行分类统计
  • 计算机网络——TCP拥塞控制原理
  • ubuntu-开机黑屏问题快速解决方法
  • DNS服务器
  • 【C++笔记】string类使用详解