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