有如下两种类型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;
}