首页 > 其他分享 >ORCA优化器浅析——DXLToPlStmt ForeignScan

ORCA优化器浅析——DXLToPlStmt ForeignScan

时间:2023-10-31 11:32:10浏览次数:30  
标签:DXLToPlStmt scan foreign ForeignScan dyn plan context table 浅析


有如下两种类型CDXLNode,通过对应的函数将CDXLNode转为对应的Plan:

  • EdxlopPhysicalForeignScan --》TranslateDXLTblScan
  • EdxlopPhysicalDynamicForeignScan --》TranslateDXLDynForeignScan
    其中比较重要的函数是ProcessDXLTblDescr【translate table descriptor into a range table entry】和CreateForeignScan【create ForeignScan plan node】,后续会详细学习一下其代码。

TranslateDXLTblScan

TranslateDXLTblScan函数将DXL table scan节点转化为TableScan Plan。流程如下所示:
1 提取形参tbl_scan_dxlnode有效信息【获取CDXLNode的m_dxl_op成员,转化为CDXLPhysicalTableScan;获取CDXLPhysicalTableScan中的m_dxl_table_descr CDXLTableDescr;通过CDXLTableDescr中的表MDId,利用md accessor获取表元数据对象IMDRelation,并将表MDId转为CMDIdGPDB;】
2 Lock any table we are to scan, since it may not have been properly locked by the parser (e.g in case of generated scans for partitioned tables)
3 translate table descriptor into a range table entry 【// translation context for column mappings in the base relation CDXLTranslateContextBaseTable base_table_context(m_mp);Index index = ProcessDXLTblDescr(dxl_table_descr, &base_table_context);
4 translate proj list and filter 【TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, &base_table_context, // translate context for the base table nullptr, // translate_ctxt_left and pdxltrctxRight, &targetlist, &qual, output_context); 5 如果表的存储类型是Foreign Table,则调用gpdb::CreateForeignScan创建ForeignScan,并填充相应的成员;其他则创建SeqScan,并填充相应的成员
6 translate operator costs

//---------------------------------------------------------------------------
//	@function: CTranslatorDXLToPlStmt::TranslateDXLTblScan
//	@doc: Translates a DXL table scan node into a TableScan node
//---------------------------------------------------------------------------
Plan *CTranslatorDXLToPlStmt::TranslateDXLTblScan(const CDXLNode *tbl_scan_dxlnode, CDXLTranslateContext *output_context, CDXLTranslationContextArray *ctxt_translation_prev_siblings) {
	// translate table descriptor into a range table entry
	CDXLPhysicalTableScan *phy_tbl_scan_dxlop = CDXLPhysicalTableScan::Cast(tbl_scan_dxlnode->GetOperator());
	const CDXLTableDescr *dxl_table_descr = phy_tbl_scan_dxlop->GetDXLTableDescr();
	const IMDRelation *md_rel = m_md_accessor->RetrieveRel(dxl_table_descr->MDId());
	
	// Lock any table we are to scan, since it may not have been properly locked by the parser (e.g in case of generated scans for partitioned tables)
	CMDIdGPDB *mdid = CMDIdGPDB::CastMdid(md_rel->MDId());
	gpdb::GPDBLockRelationOid(mdid->Oid(), dxl_table_descr->LockMode());
	
	// translation context for column mappings in the base relation
	CDXLTranslateContextBaseTable base_table_context(m_mp);
	Index index = ProcessDXLTblDescr(dxl_table_descr, &base_table_context);

	// translate proj list and filter
	CDXLNode *project_list_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexProjList];
	CDXLNode *filter_dxlnode = (*tbl_scan_dxlnode)[EdxltsIndexFilter];
	List *targetlist = NIL; List *qual = NIL;
	TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, &base_table_context,  // translate context for the base table nullptr,			  // translate_ctxt_left and pdxltrctxRight, &targetlist, &qual, output_context);

	Plan *plan = nullptr; Plan *plan_return = nullptr;

	if (IMDRelation::ErelstorageForeign == md_rel->RetrieveRelStorageType()) {
		OID oidRel = CMDIdGPDB::CastMdid(md_rel->MDId())->Oid();
		RangeTblEntry *rte = m_dxl_to_plstmt_context->GetRTEByIndex(index);
		ForeignScan *foreign_scan = gpdb::CreateForeignScan(oidRel, index, qual, targetlist, m_dxl_to_plstmt_context->m_orig_query, rte);
		foreign_scan->scan.scanrelid = index;
		plan = &(foreign_scan->scan.plan);
		plan_return = (Plan *) foreign_scan;
	} else {
		SeqScan *seq_scan = MakeNode(SeqScan);
		seq_scan->scanrelid = index;
		plan = &(seq_scan->plan);
		plan_return = (Plan *) seq_scan;
		plan->targetlist = targetlist;
		plan->qual = qual;
	}
	plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId();

	// translate operator costs
	TranslatePlanCosts(tbl_scan_dxlnode, plan);

	SetParamIds(plan);

	return plan_return;
}

TranslateDXLDynForeignScan

TranslateDXLDynForeignScan函数将DXL dynamic foreign scan节点转化为DynamicForeignScan Plan。该函数和TranslateDXLDynTblScan相似,但是其有额外的逻辑创建fdw_private_array。由于需要调用CreateForeignScan创建该array,我们需要从子分区和root分区映射qual和targelist。流程如下所示:
1 提取形参dyn_foreign_scan_dxlnode有效信息【获取CDXLNode的m_dxl_op成员,转化为CDXLPhysicalDynamicForeignScan;获取CDXLPhysicalTableScan中的m_dxl_table_descr CDXLTableDescr】
2 translate table descriptor into a range table entry 【// translation context for column mappings in the base relation CDXLTranslateContextBaseTable base_table_context(m_mp);Index index = ProcessDXLTblDescr(dyn_foreign_scan_dxlop->GetDXLTableDescr(), &base_table_context)
3 获取root dynamic scan的RTE,获取其中的表oid;获取子分区表IMdId数组,提取分区oid到oids_list中
4 create dynamic scan node dyn_foreign_scan,将分区oid列表oids_list设置到partOids
5 调用TranslateJoinPruneParamids获取动态join裁剪的信息(Info for run-time join pruning, using Partition Selector nodes;These param IDs contain additional Bitmapsets containing selected partitions.),设置到dyn_foreign_scan的join_prune_paramids成员
6 translate proj list and filter 【TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode, &base_table_context, // translate context for the base table nullptr, // translate_ctxt_left and pdxltrctxRight, &targetlist, &qual, output_context); 7 将rte的relid设置为第一个子分区的oid(set the rte relid to the child, since we need to call the fdw api which assumes we’re working with a foreign table. The root partition is not foreign!),need to lock foreign rel when calling out to CreateForeignScan
8 调用CreateForeignScan为第一个子分区创建ForeignScan。将dyn_foreign_scan的foreignscan成员设置为foreign_scan_first_part,将dyn_foreign_scan->foreignscan.scan.plan.type设置为T_DynamicForeignScan,将dyn_foreign_scan->foreignscan.scan.scanrelid设置为index
9 为每个子分区调用CreateForeignScan函数创建ForeignScan,并将其中的fdw_private追加到dyn_foreign_scan的fdw_private_list成员中
10 translate operator costs

//---------------------------------------------------------------------------
//	@function: CTranslatorDXLToPlStmt::TranslateDXLDynForeignScan
//	@doc:
//		Translates a DXL dynamic foreign scan node into a DynamicForeignScan node
//		This is similar to TranslateDXLDynTblScan, but has additional logic to
//		populate the fdw_private_array. Note that because we need to call
//		CreateForeignScan to populate this array, we need to map the qual
//		and targetlist from the child partitions from the root partition
//		While we do some of this in the executor, since we populate the
//		fdw_private for each child here, we also need mapping logic here
//---------------------------------------------------------------------------
Plan *CTranslatorDXLToPlStmt::TranslateDXLDynForeignScan(const CDXLNode *dyn_foreign_scan_dxlnode,CDXLTranslateContext *output_context, CDXLTranslationContextArray *ctxt_translation_prev_siblings) {
	// translate table descriptor into a range table entry
	CDXLPhysicalDynamicForeignScan *dyn_foreign_scan_dxlop = CDXLPhysicalDynamicForeignScan::Cast(dyn_foreign_scan_dxlnode->GetOperator());
	// translation context for column mappings in the base relation
	CDXLTranslateContextBaseTable base_table_context(m_mp);
	Index index = ProcessDXLTblDescr(dyn_foreign_scan_dxlop->GetDXLTableDescr(), &base_table_context);
	
	// rte of root dynamic scan
	RangeTblEntry *rte = m_dxl_to_plstmt_context->GetRTEByIndex(index);
	Oid oid_root = rte->relid;
	IMdIdArray *parts = dyn_foreign_scan_dxlop->GetParts();

	List *oids_list = NIL;
	for (ULONG ul = 0; ul < parts->Size(); ul++) {
		Oid part = CMDIdGPDB::CastMdid((*parts)[ul])->Oid();
		oids_list = gpdb::LAppendOid(oids_list, part);
	}
	
	// create dynamic scan node
	DynamicForeignScan *dyn_foreign_scan = MakeNode(DynamicForeignScan);
	dyn_foreign_scan->partOids = oids_list;

	OID oid_type = CMDIdGPDB::CastMdid(m_md_accessor->PtMDType<IMDTypeInt4>()->MDId()) ->Oid();
	dyn_foreign_scan->join_prune_paramids = TranslateJoinPruneParamids(dyn_foreign_scan_dxlop->GetSelectorIds(), oid_type, m_dxl_to_plstmt_context);


	// translate proj list and filter for root
	CDXLNode *project_list_dxlnode =(*dyn_foreign_scan_dxlnode)[EdxltsIndexProjList];
	CDXLNode *filter_dxlnode = (*dyn_foreign_scan_dxlnode)[EdxltsIndexFilter];
	List *targetlist = NIL; List *qual = NIL;
	TranslateProjListAndFilter(project_list_dxlnode, filter_dxlnode,
		&base_table_context,  // translate context for the base table
		nullptr,			  // translate_ctxt_left and pdxltrctxRight,
		&targetlist, &qual, output_context);

	// set the rte relid to the child, since we need to call the fdw api
	// which assumes we're working with a foreign table. The root partition is
	// not foreign!
	Oid oid_first_child = CMDIdGPDB::CastMdid((*parts)[0])->Oid(); rte->relid = oid_first_child;
	// need to lock foreign rel when calling out to CreateForeignScan
	gpdb::GPDBLockRelationOid(oid_first_child, dyn_foreign_scan_dxlop->GetDXLTableDescr()->LockMode());

	ForeignScan *foreign_scan_first_part = gpdb::CreateForeignScan(oid_first_child, index, qual, targetlist, m_dxl_to_plstmt_context->m_orig_query, rte);

	// Set the plan fields to the first partition. We still want the plan type to be a dynamic foreign scan
	dyn_foreign_scan->foreignscan = *foreign_scan_first_part;
	dyn_foreign_scan->foreignscan.scan.plan.type = T_DynamicForeignScan;
	dyn_foreign_scan->foreignscan.scan.scanrelid = index;

	Plan *plan = &(dyn_foreign_scan->foreignscan.scan.plan);
	plan->plan_node_id = m_dxl_to_plstmt_context->GetNextPlanId();
	plan->targetlist = targetlist;
	plan->qual = qual;
	
	gpdb::RelationWrapper rootRel = gpdb::GetRelation(oid_root);
	gpdb::RelationWrapper childRel = gpdb::GetRelation(oid_first_child);
	TupleDesc fromDesc = RemapAttrsFromTupDesc(RelationGetDescr(rootRel), RelationGetDescr(childRel), index, qual, targetlist);
	// populate fdw_private_list. Each fdw_private can and typically will be different for each partition
	// we have no way of knowing exactly what will be different, or which specific api calls will populate the
	// different part of fdw_private. So we have to be conservative and call everything for each partition
	// We call CreateForeignScan for each partition, and append the fdw_private to the list
	dyn_foreign_scan->fdw_private_list = NIL;
	for (ULONG ul = 0; ul < parts->Size(); ul++){
		rte->relid = CMDIdGPDB::CastMdid((*parts)[ul])->Oid();
		gpdb::RelationWrapper childRel = gpdb::GetRelation(rte->relid);
		fromDesc = RemapAttrsFromTupDesc(fromDesc, RelationGetDescr(childRel), index, qual, targetlist);
		// need to lock foreign rel when calling out to CreateForeignScan
		gpdb::GPDBLockRelationOid(rte->relid, dyn_foreign_scan_dxlop->GetDXLTableDescr()->LockMode());

		ForeignScan *foreign_scan = gpdb::CreateForeignScan(rte->relid, index, qual, targetlist,m_dxl_to_plstmt_context->m_orig_query, rte);
		dyn_foreign_scan->fdw_private_list = gpdb::LAppend(dyn_foreign_scan->fdw_private_list, foreign_scan->fdw_private);
	}
	// convert qual and targetlist back to root relation. This is used by the executor node to remap to the children
	gpdb::RelationWrapper prevRel = gpdb::GetRelation(rte->relid);
	fromDesc = RemapAttrsFromTupDesc(RelationGetDescr(prevRel), RelationGetDescr(rootRel), index, qual, targetlist);
	rte->relid = oid_root; // set the rte relid back to the root	
	
	TranslatePlanCosts(dyn_foreign_scan_dxlnode, plan); // translate operator costs
	SetParamIds(plan);

	return plan;
}


标签:DXLToPlStmt,scan,foreign,ForeignScan,dyn,plan,context,table,浅析
From: https://blog.51cto.com/feishujun/8102888

相关文章

  • ORCA优化器浅析——DXLToPlStmt[CTranslatorDXLToPlStmt]
    如上图所示是将plan_dxl转为plan_stmt的主入口函数。其主要工作就是创建plan_id_generator、motion_id_generator、param_id_generator和table_list、subplans_list,并将其设置到CContextDXLToPlStmtdxl_to_plan_stmt_ctxt中供后续流程调用;初始化CTranslatorDXLToPlStmt类,形参为MD......
  • 倾斜摄影三维模型的根节点合并注意事项浅析
    倾斜摄影三维模型的根节点合并注意事项浅析 倾斜摄影三维模型的根节点合并是构建高精度、真实感和稳定性的三维模型的关键步骤之一。在进行根节点合并时,需要注意以下几个重要的事项,以确保合并的准确性和可靠性。首先,准确的相机标定是进行根节点合并的基础。相机标定是确定相......
  • 浅析5种常见的RAID技术
    RAID RAID(RedundantArrayofIndependentDisks)全称为独立磁盘冗余阵列,简称为磁盘阵列。 RAID是指利用虚拟化存储技术把多个硬盘组合起来,成为一个或多个硬盘阵列组的技术,目的为提升性能或资料冗余,或是两者同时提升 最常用的五种RAID为RAID0、RAID1、RAID2、RAID5、......
  • 浅析SpringBoot加载配置的6种方式
    从配置文件中获取属性应该是SpringBoot开发中最为常用的功能之一,但就是这么常用的功能,仍然有很多开发者抓狂~今天带大家简单回顾一下这六种的使用方式:说明Environment对象Environment是springboot核心的环境配置接口,它提供了简单的方法来访问应用程序属性,包括系统属......
  • 浅析 es 查询文档的过程
    本文从一个示例入手,从代码层面分析elasticsearch查询文档的完整过程。新建索引cn-msg,设置3分片,1副本PUTlocalhost:9200/cn-msg{"settings":{"number_of_shards":3,"number_of_replicas":1}}写入文档POSTlocalhost:9200/cn-msg/_doc{"messag......
  • 浅析高铁客运服务质量与旅客态度——LW
    目 录1 绪论 11.1研究目的及意义 11.2研究内容 11.3研究方法 22 高铁客运服务质量的现状以及旅客心理需求对其影响 22.1高铁客运服务质量的现状 22.1.1客运设施硬件投入方面 22.1.2服务理念方面 32.2旅客心理需求对于客运服务质量的影响 32.2.1职业的影响 32.2......
  • 浅析高压开关配电室SF6环境监测系统的设计
    摘要:介绍了高压开关配电室SF6环境监测系统的组成、主要功能、工作原理及设计思想。系统应用电化学SF6浓度传感器,采用热裂解-电化学复合检测技术对高压开关配电室环境中SF6浓度实时进行监测,同时监测氧气含量、温度、湿度等参数,并且当配电室环境中SF6浓度、氧气含量超过标准时,自......
  • 飞管飞控系统仿真应用探究与浅析
    ​数字孪生技术是对真实物理实体的虚拟映射与数字化信息的应用再造,因其在产品生产制造与技术运用过程中,可将物理世界和数字世界进行实时交汇与良好互动的特性越来越受到普遍关注与广泛应用。据统计,2021年全球数字孪生市场规模为约500亿元,仍是蓝海市场。预计到2025年,全球数字孪生市......
  • 浅析 C# Console 控制台为什么也会卡死
    一:背景1.讲故事在分析旅程中,总会有几例控制台的意外卡死导致的生产事故,有经验的朋友都知道,控制台卡死一般是动了快速编辑窗口的缘故,截图如下:虽然知道缘由,但一直没有时间探究底层原理,市面上也没有对这块的底层原理介绍,昨天花了点时间简单探究了下,算是记录分享吧。二:几个疑......
  • 浅析“圆”在中国古典舞创作中的运用——以《踏歌》为例(文档)
    目录摘要IAbstractII一、绪论1(一)研究背景1(二)研究的目的及意义1(三)研究方法21.文献研究法22.经验总结法23.个案研究法2二、中国古典舞“圆”的概述3(一)中国古典舞中“圆”的认知3(二)中国古典舞中“圆”的表现形式31.静态造型中的“圆”32.动作呈现时的“圆”43.舞蹈动......